Sun ONE logo     Previous      Contents      Index      Next     
Sun ONE Portal Server Developer's Guide



Chapter 2   Using Provider API (PAPI)

This chapter provides an overview of the SunTM ONE Portal Server software Provider Application Programming Interface (PAPI) and includes instructions for developing a custom provider by extending the PAPI.

This chapter contains the following sections:

Overview of the Provider API and Desktop Servlet

Figure 2-1 shows the relationship between the various interfaces, classes, and exceptions discussed in this chapter. For detailed information on these interfaces, classes, and exceptions, see the Javadocs at http://servername:port/portal/javadocs.

Figure 2-1    The Provider Application Programming Interface (PAPI)


This figure shows the relationship between the various PAPI interfaces, classes, and exceptions discussed in this chapter. See The Provider API (PAPI) section for detailed information on these interfaces, classes, and  exceptions.

The Provider API

This section provides an overview of the interfaces (see Provider, ProviderWidths, and ProviderEditTypes), base classes (see ProviderAdapter, ProfileProviderAdapter), context (see ProviderContext), exceptions (see Exceptions), and the lifecycle of the provider.

Provider

The Provider interface clearly defines the communication channel between a container and a provider. The container may be the DesktopServlet, or another provider object. The methods defined in the Provider interface supply the basic handshaking required for a container to display the content from a provider object.

A provider can implement this interface. Clients of the Provider interface call the methods in this interface to query information or to perform an action in the provider. Such clients include, but are not limited to, the DesktopServlet and other channels (container channels).

There are essentially two types of methods in this interface:

  • Methods that handle actions - this includes getContent(), getEdit(), and processEdit() methods.
  • Methods that facilitate the handling of actions - this includes all methods that begin with is and get (minus the get* methods used for handling actions).

See the Javadocs for more information on the methods in this interface.

ProviderAdapter

The ProviderAdapter class implements aspects of the Provider using the ProviderContext. The ProviderAdapter class implements the ProviderWidths interface, which defines the provider widths, and ProviderEditTypes interface, which defines the edit types, making the constants defined in these interfaces available to classes that extend ProviderAdapter.

Developers who wish to implement a provider can extend this class or the ProfileProviderAdapter class for forward compatibility and convenient access to the ProviderContext interface.

ProfileProviderAdapter

The ProfileProviderAdapter is a subclass of the ProviderAdapter that includes convenience wrappers around some commonly used methods in the ProviderContext interface, like get/set*Property(), exists*Property(), get/setStringAttribute(), and getTemplate().

The advantages of using the wrapper methods in the ProfileProviderAdapter class as opposed to using the methods in the ProviderContext interface are the following:

  • For example, the ProviderContext.getStringProperty accepts the channel name as an argument. Since the ProfileProviderAdapter knows the channel name, it can use ProfileProviderAdapter.getStringProperty() method without providing the channel name.
  • All of the methods in ProviderContext throw a ProviderContextException. The analogous methods in ProfileProviderAdapter throw a ProviderException. For example, when implementing the getContent() method, since this method is already defined to throw a ProviderException, when a client calls ProfileProviderAdapter.getStringProperty() inside the getContent() method, you need not implement a try-catch block. However, if you use ProviderContext.getStringProperty(), you must catch the ProviderContextException.

ProviderContext

The ProviderContext interface provides information pertaining to the environment that a provider object is executing within. Such information may be specific to the user, to the web container, or be global (shared between multiple users). This interface does not define what information falls into each of these categories; this is left up to the implementation.

The ProviderContext forms the layer between the services used by the provider and the provider implementation thus isolating the provider code from specific service interfaces and implementations. It defines the service interface for the Desktop, and allows different implementation to access the actual data.

The following are some of the methods in the ProviderContext:

  • Service (data/functions pertaining to the Desktop service): getDesktopURL(), getLocaleString(), getLocale(), getDesktopType(), getLogoutURL(), getStringAttribute(), setStringAttribute(), getDefaultChannelName()
  • Servlet (data/functions pertaining to the servlet): getRequestServer(), getServletConfig()
  • Client type (data/functions pertaining to the client device type): getClientTypeProperty(), getDefaultClientType(), getClientType(), getCharset(), getClientPath(), getContentType()
  • Session:
    • Data/functions pertaining to the user's session: get/setSessionProperty(), getSessionID(), getUserID(), encodeURL()
    • Data/functions pertaining to per-client properties: get/setClientProperty()
    • Data/functions pertaining to the debug service: isDebugEnabled(), isDebugErrorEnabled(), isDebugWarningEnabled(), isDebugMessageEnabled(), debugMessage(), debugWarning(), debugError()
    • Data/functions pertaining to template access: getTemplate(), getTemplatePath(), getTemplateMostSpecificPath()
    • Data/functions pertaining to channel properties: getProviderName(), getClassName(), getNames(), get/set*Property(), exists*Property()
    • Data/functions pertaining to config properties: getStaticContentPath(), getConfigProperty()

ProviderWidths

The ProviderWidths interface defines the widths that can be returned from the Provider.getWidth() method. The width is a suggestion to a client of a provider object as to how much screen real estate should be given to display the provider's default view.

  • The provider can be displayed in a thick frame (WIDTH_THICK).
  • The provider can be displayed in a thin frame (WIDTH_THIN).
  • The provider can be displayed in a full_top frame at the top (WIDTH_FULL_TOP).
  • The provider can be displayed in a full_bottom frame at the bottom (WIDTH_FULL_BOTTOM).

ProviderEditTypes

The ProviderEditTypes interface defines the edit types that can be returned from the Provider.getEditType() method. The edit type informs a client of a provider object what it can expect to be returned from the provider's getEdit() method. The edit type can be EDIT_COMPLETE or EDIT_SUBSET.

  • EDIT_SUBSET - Indicates that the edit page is not a complete document. This value is potentially returned from getEditType() to signify that the buffer returned is only a subset of a document. This is useful for providing a common look and feel for a set of common portal pages. That is, it allows the channel's edit page to look and feel according to the container that it exists within although the same channel might appear in two different containers that have very different look and feel.
  • EDIT_COMPLETE - Indicates that the edit page is a complete document. This value is potentially returned from getEditType() to signify that the buffer returned is a complete document. This can be used in cases where the provider must have control over the entire page.

Exceptions

The Desktop expects a provider to only throw ProviderException or a subclass of the ProviderException. For correct operation, a provider must only throw expected exception type. That is:

  • Minor exceptions can be managed internally, for example, log a debug message.
  • Serious exceptions can be rethrown as an expected exception type.

Exceptions from providers are logged in /var/opt/SUNWam/debug/desktop.debug file.

ProviderException

The ProviderException is a generic superclass for all provider related exceptions.

AsciiFormInputExpectedException

The AsciiFormInputExpectedException will be thrown from Provider.processEdit method when something other than ASCII only encoded form input is sent to it.

InvalidEditFormDataException

The InvalidEditFormDataException is thrown from the Provider.processEdit method when there is an error in the data input by the user. If thrown, the Desktop will send back the same Edit page, and will attach the exception's message as a parameter to the URL. For example, if the exception is:

throw new InvalidEditFormDataException("Error Error");

the Desktop will redirect back to the same Edit page, adding the error message to the URL error parameter:

error=Error Error

The edit page wrapper then looks for the error parameter in the URL and if present, displays the message at the top of the page in red.

UnknownEditTypeException

The UnknownEditTypeException may be thrown from Provider.getEditType() method if an unknown or undefined edit type is encountered.

The Provider Life Cycle

This section describes the lifecycle of the provider per user session. Figure 2-2, Figure 2-3, and Figure 2-4 shows how the Desktop handles each user session.

A session is created at the time the user logs in to the portal Desktop and ends when they logout. The session also ends when the user session is idle, or when the session times out. For authless logins, a single session is shared amongst all clients accessing the Desktop in authless mode, and the authless session never dies.

For each request, the DesktopServlet will validate the user session; if it is a valid session, the DesktopServlet will start to process the request. If a session does not exist, the DesktopServlet will create a session.

The creation of the provider context is equivalent to the creation of a session. The container provider context object is generated and maintained, one per session. So each time a request comes in, the cached container provider context object is used. For example, in Figure 2-2, when the first initial request comes in, the container provider context is created and initialized, and when the subsequent requests come in, as in the reload and logout requests shown in Figure 2-3 and Figure 2-4 respectively, the cached container provider context is returned.

The container provider context maintains a list of its contained provider objects and their cached content locally. Contained providers are created and initialized once per session, and their content is cached in the provider context object. Subsequent requests access the cached provider objects that live inside the provider context object.

The contentChanged() and allContentChanged() methods signify that either the content for one channel or all channels has changed, respectively. This is used to remove all the cached content for the channels, as well as to clear the cached provider objects. When the requested action is logout, as shown in Figure 2-4, the DesktopServlet will redirect the request to the logout URL, and the session will be destroyed. At this point, the provider context object is removed from the cached list, and ready for garbage collection. Also, if the session expires or timesout, the provider context object is removed from the cached list.

Figure 2-2 and Figure 2-4 shows a sample of how the content and logout actions are processed. For detailed information on how the DesktopServlet and the backend providers handle each action (including content and logout), see "The DesktopServlet."

Figure 2-2    The Provider Life Cycle - Initial Request for Authenticated User


This figure shows how the Desktop handles the initial request from the client.

Figure 2-3    The Provider Lifecycle - Desktop Reload Request for Authenticated User


This figure shows how the Desktop handles a request for reload.

Figure 2-4    The Provider Lifecycle - Logout Request


This figure shows how the Desktop handles a logout request from the client.

The DesktopServlet

This section provides an overview of the DesktopServlet and discusses the relationship between the DesktopServlet and the PAPI. It provides information on how the DesktopServlet uses the various methods in the PAPI to perform the various actions (such as content, edit, process, and logout).

Introduction

The DesktopServlet co-ordinates the drawing of the Desktop, dispatches the process to the target channel based on the information stored in the underlining services, and validates the user with the iPlanetTM Directory Server Access Management Edition software.

In this sense, the DesktopServlet is a router of requests. It catches requests for content and processing, and passes them on to the specific provider object. Whenever a provider throws an exception which can not be handled by the container provider, the exception will propagate all the way up to the DesktopServlet, and the DesktopServlet will display an error page.

For detailed information on how the DesktopServlet creates and validates a user session and creates and gets the provider context object, see the "The Provider Life Cycle." The following sections only describe how the DesktopServlet handles the various actions.

DesktopServlet Actions

The DesktopServlet understands several actions. Every action has an associated channel or container name and actions are performed on the associated channel or container. Actions are passed to the servlet via request parameters. The associated channel name is also passed as a parameter.

For example, to perform an action on a channel, pass in the following parameters to the servlet:

DesktopServlet_URL?action=actionType&provider=ChannelName

Here:

  • action - indicates the type of action to take. Action can be content, edit, process, or logout.
  • provider - indicates the name of the provider to contact. The provider argument is named that way for historical reasons; the value of the provider argument is really a channel or container name. If the provider parameter is absent or null in the request, the servlet assumes it is equal to the value of the last request. For the initial request, if the provider parameter is absent, then the defaultChannelName from the Desktop service will be used.

The action and provider parameters are not required; if they are absent, the default to action is content, and provider is the value set in the Desktop service for the default channel.

The content, edit, and process actions map directly to method calls into the PAPI. For Desktop actions that map to PAPI method calls, the servlet passes an HTTP request and response object to the respective provider method. These objects are not the same objects passed into the servlet. The request and response objects passed to provider objects from the servlet are wrappers around the original request and response passed into the servlet. This is because there is certain functionality that is available from request and response objects that is not applicable to a provider. See the Javadocs for the Provider interface for more information.

The HTTP parameters in the original request object are processed before they are copied to the wrapper servlet request and response objects. As part of this processing, the parameters are decoded from the character set encoding used to represent the page into Unicode that is used in Java String objects. Therefore, the parameters that are passed to the providers are all stored as Unicode, and the provider does not have to do any decoding of its own related to the character encoding for the page.

Action content

When the action is content, the DesktopServlet gets the named channel's main content. When the DesktopServlet receives a request where the action is content, to perform the content action on the channel, it takes the following parameters:

DesktopServletURL?action=content&provider=ChannelName[&last=false]

The content action maps directly to the following method calls in the PAPI: ProviderContext.getDefaultChannelName(), Provider.isPresentable(), and Provider.getContent().

The flowchart in Figure 2-5 shows the various methods executed in the backend to process the content action. When the client makes a request for content (say after login), the DesktopServlet:

  1. Determines the provider responsible for generating the requested content.
  2. If provider is null, it uses the default channel name (stored in the DefaultChannelName attribute) to get the provider. The default channel name is an iPlanet Directory Server Access Management Edition software attribute (DefaultChannelName) in the Desktop service, which is set to the top most container that represents the whole Desktop view. The default channel name is set to the current target provider value when the request parameter last equals to true. If the provider is not null, the DesktopServlet gets the provider responsible from the HTTP parameter in the URL for generating the requested content.

  3. Once the provider is determined, the provider's isPresentable() method is invoked to determine whether or not the provider can be presented to the requesting client.
  4. If the provider is determined to be not presentable, an error is thrown on the Desktop. If the provider can be presented, the provider's getContent() method fetches the content for display on the client's Desktop.

  5. When last is set to false, the DesktopServlet will not set the last channel to the value of the provider parameter. If not specified, the default setting is last=true, and the last accessed channel is set to the value of the provider parameter.
  6. The next time when the action is content, the DesktopServlet provider parameter will be used to fetch the content. If the provider parameter is absent in the request URL, the DesktopServlet assumes it is equal to the value of the last request.

Figure 2-5    DesktopServlet content Action


This figure shows how the DesktopServlet handles the content action.

Action edit

When the action is edit, the DesktopServlet gets the named channel's editable parameters. When the DesktopServlet receives a request where the action is edit, to perform the edit action, it takes the following parameters:

DesktopServletURL?action=edit&provider=ChannelName for backward compatibility

The DesktopServlet checks the edit types based on the values defined in the ProviderEditTypes interface.

The flowchart in Figure 2-6 shows the various methods executed in the backend to process the edit action. When the client makes a request to edit the channel's editable parameters, the DesktopServlet:

  1. Determines the provider responsible for generating the requested content.
  2. If provider is null, it uses the default channel name to get the provider. Default or last accessed channel is not assumed when action equals edit. Otherwise, the DesktopServlet gets the provider responsible specified in the URL.

  3. Once the provider is determined, the provider's isEditable() method is invoked to determine whether or not the provider can be edited by the user.
  4. The DesktopServlet gets the named channel's isEditable property. If the provider is determined to be not editable, an exception is thrown on the Desktop. If the provider can be edited, the DesktopServlet also checks the editType for that channel. The editType is a channel property that can be retrieved via the Provider.getEditType() method. The provider's getEditType() method is invoked to determine the type of edit page to return on the Desktop.

  5. If the provider's edit type is:
    • EDIT_COMPLETE, the provider's getEdit method is invoked and the Edit page for the channel is returned on the Desktop.
    • EDIT_SUBSET, the edit container's provider name will be detected, and then the edit container's getEdit() method will be invoked. The edit container's provides a common look and feel of the edit page for all the channels that it contains. After it generates the markup for the common look and feel, it delegates the process to the target channel's getEdit() method. That is, the request parameter targetprovider stores the target channel's name. Based on the name, the target channel's provider object is retrieved and the target channel's getEdit method is called.

Figure 2-6    DesktopServlet edit Action


This figure shows how the DesktopServlet handles the edit action.

Action process

The process action allows the named channel to process URL parameters and form data, typically that of the channel's edit form. When the DesktopServlet receives a request where the action is process, to perform the process action, it takes the following URL parameters:

DesktopServletURL?action=process&provider=channelName

When the DesktopServlet receives a request where the action is process, the DesktopServlet:

  1. Looks at the parameters to identify which provider will handle the action, through the provider's processEdit() method.
  2. The processEdit() method is called to process the edit page generated from the getEdit() method. The request passed in contains the parameters.

  3. Re-directs to the URL returned from the provider's processEdit() method.
  4. If there is an InvalidEditFormDataException, the DesktopServlet will redirect the browser back to the channel's edit page and include a URL parameter error so that the channel may display the cause of the exception to the user. That is, the DesktopServlet will get the error message and generate a new request as follows:

    ?action=edit&provider=providername&error=errormessage

Figure 2-7    DesktopServlet process Action


This figure shows how the DesktopServlet handles the process action.

Action logout

The logout action ends the user session. When the DesktopServlet receives a request where the action is logout, to perform the logout action, it takes the following parameters:

DesktopServletURL/dt?action=logout

When the DesktopServlet receives a request for the logout action, it redirects the browser to a URL defined by the iPlanet Directory Server Access Management Edition software iplanet-am-platform-logout-url attribute in the Platform service. By default, this attribute has the value /amserver/logout. But, If this is set to something that does not terminate the user's session, such as a static HTML page, then /portal/dt?action=logout will not terminate the user session.

Overview of Implementing a Custom Provider

This section describes the development process for a Sun ONE Portal Server provider. It touches on the public APIs available for integrating an application with the Sun ONE Portal Server software Desktop via the provider.

Before developing a custom provider, determine whether or not an existing provider can be used to serve the content. If none of the PAPIs can be used as is, determine whether or not the content can be presented by overriding one or more methods of the PAPI (see chapters 3 and 4 for information on overriding specific methods in the class file).

To develop a provider, you must start by:

Defining Specific Requirements and Functionality

Before beginning to develop a provider, determine the application specific requirements, content source, and properties of the provider.

Application Specific Requirements

  • Determine the nature of the provider. Will this provider be a building block provider or will this be a content provider? The content providers are special purpose providers and the building block providers are general purpose providers. For example, the bookmark channel is special purpose, where XMLProvider is general purpose. That is, the XMLProvider can be used, in general, to connect to an XML source and translate it via XSLT to some markup language.
  • Determine whether or not the content made available via this provider will be:
    • Preset for the user by the administrator thus making it unmodifiable by the end-user. This will determine the logistics of the isEditable() method.
    • Preconfigured for the user by the administrator, but editable by the end-user (determines the logistics of the getEdit() method). If editable, determine:
      • What will and what will not be editable. That is, determine the parameters that will be configurable and those that will not be editable.
      • The type of edit form to present - EDIT_SUBSET or EDIT_COMPLETE. The advantage of using EDIT_SUBSET is that the EditContainer provides a common look and feel for all providers that have EDIT_SUBSET edit type. Using EDIT_COMPLETE edit type provides more control over the overall look and feel for the edit page.

      This will determine the logistics of the getEditType() and the processEdit() method.

  • Determine the theme requirements for the content made available via this provider. A theme is a collection of visual elements, such as background color font face, and so on, to be used when displaying the provider's content. The themes are stored as global properties in the oranization level's display profile. Providers can use these properties in their templates or JSPs to achieve a global look and feel of the presentation. For use with JSP, theme properties can be accessed from a series of tag libraries. Non-JSP channels can access theme properties via the Theme java class. See the Javadocs for more detail.

Presentation Method

Determine whether the provider will fetch content from template files or JSPs. This will determine the logistics of the getTemplate() method.

Channel template files are stored in a directory based on the name of the channel, or the name of the provider that is used by the channel. The channel directory for the provider is created under the template root directory. By default, this will be:

/etc/opt/SUNWps/desktop/desktoptype/channeldirectory/templatefiles

Some of the JSPs are stored in /etc/opt/SUNWps/desktop/default/provider directory and some are stored in /etc/opt/SUNWps/desktop/default directory. Storing the files in the /etc/opt/SUNWps/desktop/default directory allows the files to be shared among multiple channels.

The directory search order for the template and JSP files is as follows:

type_locale/channelname/clientPath

type_locale/provider/clientPath

type_locale/channelname

type_locale/provider

type_locale/clientPath

type_locale

type/channelname/clientPath

type/provider/clientPath

type/channelname

type/provider

type

default_locale/channelname/clientPath

default_locale/provider/clientPath

default_locale/channelname

default_locale/provider

default_locale

default/channelname/clientPath

default/provider/clientPath

default/channelname

default/provider

default/clientPath

default

templateroot

Where

  • type is the value of the Desktop template type property
  • locale is the user's locale
  • channelname is the name of the channel
  • provider is the provider name
  • clientPath is an optional file-path containing client-specific templates
  • templateroot is, by default, /etc/opt/SUNWps/desktop

If there is no clientPath specified, then the directory search order is as follows:

type_locale/channelname

type_locale/provider

type_locale

type/channelname

type/provider

type

default_locale/channelname

default_locale/provider

default_locale

default/channelname

default/provider

default

templateroot

The root of the search directory (default value of /etc/opt/SUNWps/desktop/) can be changed by modifying the templateBaseDir property in the desktopconfig.properties file.

Provider-specific Properties

Properties contain configuration information for the provider and the provider's channels. A provider can define any number of specific properties.

Properties in Display Profile

Provider specific properties are defined in the display profile. The following is the structure of the properties in the display profile:


<Properties>
    <Collection>
        ...subset of property definitions
    </Collection>
    <Integer>...</Integer>
    <String>...</String>
    <Boolean>...</Boolean>
</Properties>

Properties can be of types atomic (string, integer, boolean) or compound (collection, locale). See Chapter 5 of the Sun ONE Portal Server 6.0 Administrator's Guide for more information on the display profile merge semantics.

Typically, the custom provider will not modify the required properties. When properties are updated, they are set in the user's display profile by the administrator via the administration console. However, values can only be set on existing properties.

If the provider class does not extend the ProfileProviderAdapter and implements the Provider interface, then the get*Property() methods can not be used directly, and the display profile properties are not accessible. If the provider class implements the Provider interface directly, then no properties are required. Properties can be hardcoded return values or can come from another source such as a database.

However, some provider properties are required if the provider implementation extends ProviderAdapter or ProfileProviderAdapter. Table 2-1 describes the mandatory properties for a provider. The mandatory properties described in Table 2-1 are for a provider extending the ProviderAdapter or the ProfileProviderAdapter classes. If you are implementing the Provider interface directly, you can avoid the use of the display profile and the required properties outlined in Table 2-1.

Table 2-1    Required Provider Properties

Property

Description

title

Title of the Provider

description

Description of the Provider

refreshTime

Time interval for cached content

isEditable

Can the Provider be edited

editType

Type of edit form to use

width

Width of the Provider

helpURL

URL of a help page for this Provider

Channel definitions that use a provider can overwrite the properties of that provider. At runtime, channel property values can be affected by the display profile merge process. See Sun ONE Portal Server 6.0 Administrator's Guide for detailed information on the display profile merge process.

See Chapter 4 of the Sun ONE Portal Server 6.0 Desktop Customization Guide for detailed information on the Display Profile properties.

Properties in Resource bundle

Strings defined in the provider's resource bundle properties file are displayed on the user's Desktop and they are not the channel's properties (such as the channel Title). Strings are defined in the resource bundle to enable localization.

Properties that do not need to be customized by the administrator or the end-user must be included in the resource bundle. For example, properties that need to be localized, are read-only at runtime, and properties that do not change per channel should be included in a resource bundle. Properties that need to be localized, are settable, and properties that change per channel should be in the display profile.

By default, a provider's resource bundle properties file is stored in /etc/opt/SUNWps/desktop/classes.

Resource bundles that are needed by the custom providers should be copied into the provider class base directory. Resource bundles should be placed as individual files and cannot exist inside the JAR file under the provider class base directory.

Resource bundles are given a base name that equals to the name of the display profile provider definition that they are associated with. Typically, this is the relative (non-fully qualified) class name of the Java class file that implements the provider.

To get the name of the resource bundle in your custom provider, call ProviderContext.getProviderName(). This value can then be used as the baseName argument to ResourceBundle.getBundle(). If you are extending ProviderAdapter, simply call getResourceBundle().

Developing the Provider Class File

This section includes information on the requirements for developing a provider's class file.

Provider Class File Location

The provider classes must be deployed in the provider class base directory specified in the file /etc/opt/SUNWps/desktop/desktopconfig.properties. By default, the provider class base directory is /etc/opt/SUNWps/desktop/classes.

For instant deployment, the JAR file has to be dropped in the directory mentioned in providerClassBaseDir variable in the desktopconfig.properties file.

Provider Class File

The custom provider classes and all other custom classes that the provider classes reference can be bundled into a JAR file and placed in the provider class base directory. They can also exist as .class files in the provider class base directory.

If a class exists in a file and is also present in a JAR file under the provider class base directory, the class existing as a file under the provider class base directory is given the first preference. Once the class is found as a file, no attempt is made to check for its existence in a JAR file.

If a class file exists in two JAR files under the provider class base directory, the class is picked up from one of them at runtime. Do not duplicate classes in multiple JAR files.

When compiling the class file, the Servlet API (BaseDir/SUNWam/lib/servlet.jar) and PAPI (BaseDir/SUNWps/sdk/desktop/desktopsdk.jar) are required in the CLASSPATH. That is, when compiling a *Provider.java file, you must type:

javac -d /etc/opt/SUNWps/desktop/classes -classpath BaseDir/SUNWps/sdk/desktop/desktopsdk.jar:BaseDir/SUNWam/lib/servlet. jar *Provider.java

Creating XML Fragments for Display Profile

A provider must be defined in the display profile before it can be used. This definition associates the provider with its class file implementation and includes the provider's properties. That is, the provider's display profile fragment should include default values for all the properties that are used in the provider.java file. For example, if the provider.java file contains getStringProperty("color"), the provider's display profile fragment should include a default value for color.

The provider's XML fragment in the display profile must adhere to the following structure:



Code Example 2-1    Display Profile Structure of a Provider

<DisplayProfile>
    <Providers>
        <Provider name="providername"
            class="provider class name">
            <Properties>...</Properties>
        </Provider>
    </Providers>
</DisplayProfile>

See Chapter 2 of the Sun ONE Portal Server 6.0 Desktop Customization Guide for more information on the display profile provider definitions.

The provider definition is the template that decides the properties for the provider's channels. However, the display profile definition for the channel ultimately decides the values for the channel's properties. When a channel sets a property, the property is set in the channel and not in the provider's properties. When defining channel properties in the display profile, include only those properties where the provider defaults are not applicable.

The provider's channel XML fragment in the display profile must adhere to the following structure:



Code Example 2-2    Display Profile Structure of a Channel

<DisplayProfile>
    <Channels>
        <Channel name="channelname" provider="providername">
            <Properties>...</Properties>
        </Channel>
    </Channels>
</DisplayProfile>

See Chapter 3 of the Sun ONE Portal Server 6.0 Desktop Customization Guide for more information on the display profile channel definitions.

The channel definition in the display profile need not have the <Container> tag unless the channel is defined within a container. Channel definitions in the display profile can be encapsulated inside a container. The advantage of this is that it provides name scoping so that channels with the same name do not collide.

Unless a channel is referenced directly from the DesktopServlet URL, in which case it need not be encapsulated inside a container, you must reference a channel from some container in order to see it.

The container within which the channel will operate must adhere to the following structure:



Code Example 2-3    Display Profile Structure of a Container

<DisplayProfile>
    <Channels>
        <Container>
            <Properties>...</Properties>
            <Available>...</Available>
            <Selected>...</Selected>
            <Channel>...</Channel>
        </Container>
    </Channels>
</DisplayProfile>

To upload display profile changes, type:

  • dpadmin add -u dn_amadmin -w password -d distinguishedname provider.xml if the display profile XML fragment was stored in a separate file
  • dpadmin modify -u dn_amadmin -w password -d distinguishedname provider.xml if an existing display profile document was directly edited

where provider.xml contains the display profile XML fragment for the provider. See Chapter 12 of the Sun ONE Portal Server 6.0 Administrator's Guide for more information on the dpadmin command. See Chapter 5 of the Sun ONE Portal Server 6.0 Administrator's Guide for more information on administering Sun ONE Portal Server software display profile.

Installing the Provider

This section provides information on:

Installing a provider

This section includes instructions for installing the provider using the par utility and manually.

Installing Manually

To install the provider manually:

  1. Compile the provider class file and copy the file to provider class base directory which, by default, is /etc/opt/SUNWps/desktop/classes. Or copy the file to the location specified in the /etc/opt/SUNWps/desktop/desktopconfig.properties file.
  2. Copy the resource bundle files, if any, to /etc/opt/SUNWps/desktop/classes directory.
  3. Develop the JSPs and templates for the provider and copy the files to the template root directory which, by default, is /etc/opt/SUNWps/desktop/default.
  4. Develop and upload the display profile XML fragments for the provider, the provider's channels, and the container within which the channels will operate, if any.

Transporting Using the par Utility

Utilize the par utility for deploying channels and providers. For example, if you have the provider associated files in the development environment ready for deployment into the production environment, use the following command to build the channel and provider into a par file which can then be deployed on the system.

par export -r dn_amadmin -p password -v provider.par dn exportfile

To deploy the par file into the system, use the following command:

par import -r dn_amadmin -p password -v provider.par dn provider=providername,channel=channelname

See Chapter 12 of the Sun ONE Portal Server 6.0 Administrator's Guide for more information on the par utility.

Provider Class Loader

The provider class loader in the Sun ONE Portal Server software is used to load the classes. Each channel/container is loaded with a separate provider class loader object. This enables on-the-fly reloading of classes when they change.

In order to upload a new version of a custom provider class without restarting the server:

  • If the original class existed in a JAR file, rebuild the JAR with the new version of the class file and copy the JAR into the provider class base directory.
  • Or,

  • Just copy the new version of the class as a file into the provider class base directory.

In order to reload a property file, that is being used by a custom provider, with a modified property inside a resource bundle in the provider class base directory without restarting the server, change the last modified time on the custom provider class. If the custom provider class exists:

  • In a JAR file, use the command touch JAR-file-name
  • As a file, use the command touch class-file-name

The presence of the provider class loader has an impact on the design of a multi-channel application in the Sun ONE Portal Server software Desktop. When an object reference is cast from one type to another, not only does the class of the variable need to match the class of the object, the two associated class loaders have to be the same too. This constraint can show up when objects are being passed between different channels in the Desktop.

For example, consider a portal application that uses two providers, one for a container and the other for several leaf channels. Call these providers HeadlineContainerProvider and HeadlineProvider. The container provider performs some common processing for the leaf channels, and then the leaf channels access the container object to get the results of that processing. One way to implement this would be to have the HeadlineContainerProvider put a reference to itself into the request object. For example, in the HeadlineContainerProvider.getContent, do:

request.setAttribute("HeadlineContainer", this);

Then, in the HeadlineProvider.getContent method, do:

HeadlineContainerProvider hcp = (HeadlineContainerProvider)request.getAttribute("HeadlineContainer" );

This example will compile, but when the desktop loads and runs this code, the HeadlineProvider will cause a ClassCastException on this second statement. The reason is that the HeadlinerContainerProvider object is loaded by one provider class loader object and the HeadlineProvider object is loaded by a different class loader object. The class loader for HeadlineProvider also loads HeadlineContainerProvider because HeadlineProvider references HeadlineContainerProvider. So we have:

  • container1 of type HeadlineContainerProvider loaded by loader1
  • channel1 of type HeadlinerProvider loaded by loader2

The HeadlineContainerProvider class is also loaded by loader2 because of the reference to it in the HeadlineProvider code. The cast is from the object container1 (loaded by loader1) to the variable hcp (of type HeadlineContainerProvider loaded by loader2). Thus the cast fails because the class loaders are not the same.

To understand a case where the classes could actually be different, consider the case where container1 is loaded at time 0, and channel1 is loaded at time 10, but the HeadlineContainerProvider class files was updated on disk at time 5. This means that channel1 will be running with a different version of the HeadlineContainerProvider class than that being used for container1. The case must fail because the class versions are different.

To implement this type of application, an alternate solution is to pass an object as a request or session property that has a class that is not in the /etc/opt/SUNWps/desktop/classes directory, such as String, Set, or Hashmap. These classes are never reloaded except when the server restarts. The data that you store in that object becomes a contract between different versions of the classes that use that object.

Instructions for Extending the PAPI

This section includes instructions for setting up a sample HelloWorldProvider that will display a message ("Hello World") by:

Three examples are provided for developing a sample HelloWorldProvider. This is to illustrate the fundamentals of how the Provider interface works first, followed by examples of how ProviderAdapter and ProfileProviderAdapter classes make the implementation easier. Use the instructions (provided with the sample HelloWorldProvider) for developing your custom provider by extending any one of these APIs.

Implementing the Provider Interface

The Provider interface defines the interface for implementing the provider component of a channel. If you want to directly implement the Provider interface, code should be written to implement all of the methods in this interface. You will not have access to the ProviderContext and the properties in the display profile cannot be accessed. If you are implementing this interface, it's up to the provider implementation to get the properties.

This section provides the instructions for creating a custom provider by implementing the Provider interface. The process described here includes creating a sample HelloWorldProvider that prints "Hello World!" on the provider's channel.

  1. Create a new java class which implements the Provider interface.
  2. For the sample HelloWorldProvider, create the class file as shown in Code Example 2-4.



    Code Example 2-4    HelloWorldProviderP.java File

    package custom;

    import java.net.URL;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    import com.sun.portal.providers.Provider;
    import com.sun.portal.providers.ProviderException;
    import com.sun.portal.providers.UnknownEditTypeException;

    public class HelloWorldProviderP implements Provider {
        public void init(
            String name, HttpServletRequest req
        ) throws ProviderException { }

        public StringBuffer getContent(
            HttpServletRequest request, HttpServletResponse response
        ) throws ProviderException {
            return new StringBuffer("Hello World!");
        }

        public java.lang.StringBuffer getContent(java.util.Map m) throws ProviderException {
            return new StringBuffer("Hello, world!");
        }

        public StringBuffer getEdit(HttpServletRequest request, HttpServletResponse response) throws ProviderException {
            // Get the edit page and return it is as a StringBuffer.
            return null;
        }

        public java.lang.StringBuffer getEdit(java.util.Map m) throws ProviderException {
            return null;
        }

        public int getEditType() throws UnknownEditTypeException {
            return 0;
        }

        public URL processEdit(HttpServletRequest request, HttpServletResponse response) throws ProviderException {
            return null;
        }

        public java.net.URL processEdit(java.util.Map m) throws ProviderException {
            return null;
        }

        public boolean isEditable() throws ProviderException {
            return false;
        }

        public boolean isPresentable() {
            return true;
        }

        public java.lang.String getTitle() throws ProviderException {
            return "HelloWorld Channel";
        }

        public java.lang.String getName() {
            return "HelloWorld!";
        }

        public java.lang.String getDescription() throws ProviderException {
            return "This is a sample HelloWorld channel";
        }

        public java.net.URL getHelp(javax.servlet.http.HttpServletRequest req) throws ProviderException {
            return null;
        }

        public long getRefreshTime() throws ProviderException {
            return 0;
        }

        public int getWidth() throws ProviderException {
            return 0;
        }
    }

  3. Compile the class and put it in the user defined class directory.
  4. The default directory for the class file is /etc/opt/SUNWps/desktop/classes. To compile the HelloWorldProviderP.java file, type:

       javac -d /etc/opt/SUNWps/desktop/classes -classpath BaseDir/SUNWps/sdk/desktop/desktopsdk.jar:BaseDir/SUNWam/lib/servlet.jar HelloWorldProviderP.java

  5. Define the new provider and provider's channel definition in a temporary XML file.
  6. The sample HelloWorldProvider XML fragments for the provider in the HelloProviderP.xml and XML fragments for the channel in the HelloChannelP.xml files are shown in Code Example 2-5 and Code Example 2-6 respectively.



    Code Example 2-5    HelloProviderP.xml File

    <Provider name="HelloWorldProviderP" class="custom.HelloWorldProviderP">
        <Properties>
        </Properties>
    </Provider>



    Code Example 2-6    HelloChannelP.xml File

    <?xml version="1.0" encoding="utf-8" standalone="no"?>
    <!DOCTYPE DisplayProfile SYSTEM "jar://resources/psdp.dtd">

    <Channel name="HelloWorldP" provider="HelloWorldProviderP">
        <Properties>
            <String name="title" value="Hello World Channel"/>
            <String name="description">This is a test of the hello world provider</String>
            <String name="width" value="thin"/>
            <String name="message" value="Hello World Test!!! - non-localized "/>
            <Locale language="en" country="US">
                <String name="message" value="Hello World - I am speaking English in the United States!!!"/>
            </Locale>
        </Properties>
    </Channel>

  7. Upload the provider and channel XML fragments using the dpadmin command.
  8. For the sample HelloWorldProvider, upload the HelloProviderP.xml and HelloChannelP.xml XML fragments using the dpadmin command. That is, for example, type:

    dpadmin add -u dn_amadmin -w password -d distinguishedname HelloProviderP.xml

    dpadmin add -u dn_amadmin -w password -d distinguishedname HelloChannelP.xml

  9. Access the channel from a browser. To access, type the following URL in your browser:
  10. http://hostname:port/portal/dt?provider=HelloWorldP

Extending the ProviderAdapter Class

As the ProviderAdapter class has implemented many of the methods of the Provider interface, the custom provider can extend ProviderAdapter and can override some of the methods or define any new methods. The advantage of extending this class versus implementing the Provider interface directly is that you will maintain forward compatibility as additions are made to the PAPI. Existing code will by default call the methods in this class. Also, the ProviderAdapter contains default implementations of methods in the Provider interface that you can use.

This section provides the instructions for creating a custom provider by extending the ProviderAdapter class. The process described here includes creating a sample HelloWorldProvider that prints "Hello World!" on the provider's channel.

  1. Create a new java class which extends the ProviderAdapter class.
  2. For the sample HelloWorldProvider, create the class file as shown in Code Example 2-7.



    Code Example 2-7    HelloWorldProviderPA.java File

    package custom;

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import com.sun.portal.providers.ProviderAdapter;
    import com.sun.portal.providers.ProviderException;

    public class HelloWorldProviderPA extends ProviderAdapter {
        public java.lang.StringBuffer getContent (HttpServletRequest request, HttpServletResponse response) throws ProviderException {
            return new StringBuffer("Hello, world!");
        }
    }

  3. Compile the class and put it in the user defined class directory.
  4. The default directory for the class file is /etc/opt/SUNWps/desktop/classes. To compile the HelloWorldProviderPA.java file, type:

    javac -d /etc/opt/SUNWps/desktop/classes -classpath BaseDir/SUNWps/sdk/desktop/desktopsdk.jar:BaseDir/SUNWam/lib/servlet.jar HelloWorldProviderPA.java

  5. Define the new provider and provider's channel definition in a temporary XML file.
  6. Use the dpadmin command to upload the display profile changes to these files.

    The sample HelloWorldProvider XML fragment for the provider in the HelloProviderPA.xml is shown in Code Example 2-8.



    Code Example 2-8    HelloProviderPA.xml File

    <?xml version="1.0" encoding="utf-8" standalone="no"?>
    <!DOCTYPE DisplayProfile SYSTEM "jar://resources/psdp.dtd">

    <Provider name="HelloWorldProviderPA" class="custom.HelloWorldProviderPA">
        <Properties>
            <String name="title" value="*** TITLE ***"/>
            <String name="description" value="*** DESCRIPTION ***"/>
            <String name="refreshTime" value="0"/>
            <Boolean name="isEditable" value="true"/>
            <String name="editType" value="edit_subset"/>
            <String name="width" value="thin" advanced="true"/>
            <String name="helpURL" value="desktop/HelloWorld.htm" advanced="true"/>
            <String name="fontFace1" value="Sans-serif"/>
            <String name="productName" value="Sun ONE Portal Server"/>
            <Collection name="aList">
                <String value="i'm aList"/>
            </Collection>
            <String name="message" value="Sample Hello World Provider"/>
        </Properties>
    </Provider>

  7. Upload the provider XML fragments using the dpadmin command.
  8. For the sample HelloWorldProvider, upload the HelloProviderPA.xml file XML fragments using the dpadmin command. That is, for example, type:

    dpadmin add -u dn_amadmin -w password -d distinguishedname HelloProviderPA.xml

  9. Include the new channel in one of the existing containers.
  10. The sample HelloWorldProvider will be displayed only in a Table Desktop layout. To add the channel in the JSPTableContainer from the administration console, follow instructions in section "To Add a Channel or Container Channel" in Chapter 5 of the Sun ONE Portal Server 6.0 Administrator's Guide. To add the channel in the JSPTableContainer manually:

    1. Add the HelloWorldProvider channel XML fragment (in the HelloChannelPA.xml file) for the JSPTableContainer as shown in Code Example 2-9.


    2. Code Example 2-9    HelloChannelPA.xml File

      <?xml version="1.0" encoding="utf-8" standalone="no"?>
      <!DOCTYPE DisplayProfile SYSTEM "jar://resources/psdp.dtd">

      <Container name="JSPTableContainer" provider="JSPTableContainerProvider">
          <Properties>
              <Collection name="Personal Channels">
                  <String value="HelloWorldPA"/>
              </Collection>
              <Collection name="channelsRow">
                  <String name="HelloWorldPA" value="3"/>
              </Collection>
              <Collection name="channelsColumn">
                  <String name="HelloWorldPA" value="3"/>
              </Collection>
          </Properties>

          <Available>
              <Reference value="HelloWorldPA"/>
          </Available>
          <Selected>
              <Reference value="HelloWorldPA"/>
          </Selected>
          <Channels>
              <Channel name="HelloWorldPA" provider="HelloWorldProviderPA">
                  <Properties>
                      <String name="message" value="Hello World Test!!! - non-localized "/>
                  </Properties>
              </Channel>
          </Channels>
      </Container>

    3. Use the dpadmin add subcommand to add the channel to a container. If you do not specify a parent object with the -p option, the channel is added at the root level.
    4. dpadmin modify -m -p "JSPTableContainer" -u dn_amadmin -w password -d distinguishedname HelloChannelPA.xml

  11. Access the HelloWorld channel inside the JSPTableContainer. To access, type the following URL in your browser:
  12. http://hostname:port/portal/dt?provider=JSPTableContainer/HelloWorldPA

Extending the ProfileProviderAdapter Class

ProfileProviderAdapter has the default implementation of the Provider interface, and some convenient methods that allow the providers to get the channel data from the ProviderContext object. Developers who wish to create a new provider can take advantage of this, by extending the ProfileProviderAdapter class, and create their customized provider class.

Example 1

This section provides the instructions for creating a custom provider by extending the ProfileProviderAdapter. The process includes creating a sample provider that prints "Hello World!" on the provider's channel.

The following sample HelloWorldProvider reads a string property from the user's display profile and allows the user to edit the string.

  1. Create a new java class which extends the ProfileProviderAdapter class.
  2. For the sample HelloWorldProvider, create the class file as shown in Code Example 2-10.



    Code Example 2-10    HelloWorldProviderPPA1.java File

    package custom;

    import java.net.URL;
    import java.util.Hashtable;
    import java.util.ResourceBundle;
    import javax.servlet.http.HttpServletRequest;

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    import com.sun.portal.providers.ProfileProviderAdapter;
    import com.sun.portal.providers.ProviderException;

    public class HelloWorldProviderPPA1 extends ProfileProviderAdapter {
        private ResourceBundle bundle = null;

        // Implement the getContent method
        public StringBuffer getContent(
            HttpServletRequest req, HttpServletResponse res
        ) throws ProviderException {
            Hashtable tags = new Hashtable();
            if (bundle == null) {
                bundle = getResourceBundle();
            }
            tags.put("welcome", bundle.getString("welcome"));
            tags.put("message", getStringProperty("message", true));
            tags.put("properties", getMapProperty("aMap", true));
            StringBuffer b = getTemplate("content.template", tags);
            return b;
        }

        // Implement the getEdit method
        public StringBuffer getEdit(
            HttpServletRequest req, HttpServletResponse res
        ) throws ProviderException {
                Hashtable tags = new Hashtable();
                tags.put("message", getStringProperty("message"));
                tags.put("properties", "");
                StringBuffer b = getTemplate("edit.template", tags);
                return b;
        }

        // Implement the processEdit method
        public URL processEdit (
            HttpServletRequest req, HttpServletResponse res
        ) throws ProviderException {
            String message = req.getParameter("message");
            if(message != null) {
                if (!message.equals(getStringProperty("message"))) {
                    // Set the new message
                    setStringProperty("message",message);
                    return null;
                }
                } else {
                    setStringProperty("message","");
                }
                return null;
            }
        }
    }

  3. Compile the class and put it in the user defined class directory.
  4. The default directory for the class file is /etc/opt/SUNWps/desktop/classes. To compile the HelloWorldProviderPPA1.java file, type:

    javac -d /etc/opt/SUNWps/desktop/classes -classpath BaseDir/SUNWps/sdk/desktop/desktopsdk.jar:BaseDir/SUNWam/lib/servlet.jar HelloWorldProviderPPA1.java

  5. Define the new provider and provider's channel definition in a temporary XML file.
  6. The sample HelloWorldProvider XML fragment for the provider in the HelloProviderPPA1.xml file and the XML fragment for the channel in the HelloChannelPPA1.xml file are shown in Code Example 2-11 respectively.



    Code Example 2-11    HelloProviderPPA1.xml File

    <?xml version="1.0" encoding="utf-8" standalone="no"?>
    <!DOCTYPE DisplayProfile SYSTEM "jar://resources/psdp.dtd">

    <Provider name="HelloWorldProviderPPA1" class="custom.HelloWorldProviderPPA1">
        <Properties>
            <String name="title" value="*** TITLE ***"/>
            <String name="description" value="*** DESCRIPTION ***"/>
            <String name="refreshTime" value="0"/>
            <Boolean name="isEditable" value="true"/>
            <String name="editType" value="edit_subset"/>
            <Collection name="aMap">
                <String name="title" value="Ramag"/>
                <String name="desc" value="My Map"/>
                <Boolean name="removable" value="true"/>
                <Boolean name="renamable" value="true"/>
            </Collection>
            <String name="message" value="Hello World Test Provider"/>
        </Properties>
    </Provider>



    Code Example 2-12    HelloChannelPPA.xml File

    <?xml version="1.0" encoding="utf-8" standalone="no"?>
    <!DOCTYPE DisplayProfile SYSTEM "jar://resources/psdp.dtd">

    <Channel name="HelloWorldPPA1" provider="HelloWorldProviderPPA1">
        <Properties>
            <String name="title" value="Hello World Channel"/>
            <String name="description">This is a test of the hello world provider</String>
            <String name="width" value="thin"/>
            <String name="message" value="Hello World Test!!! - non-localized "/>
            <Locale language="en" country="US">
                <String name="message" value="Hello World - I am speaking English in the United States!!!"/>
            </Locale>
        </Properties>
    </Channel>

  7. Upload the provider and channel XML fragments using the dpadmin command.
  8. For the sample HelloWorldProvider, upload the HelloProviderPPA1.xml file and HelloChannelPPA1.xml file XML fragments using the dpadmin command. That is, for example, type:

    dpadmin add -u dn_amadmin -w password -d distinguishedname HelloProviderPPA1.xml

    dpadmin add -u dn_amadmin -w password -d distinguishedname HelloChannelPPA1.xml

  9. Include the provider's channel in one of the existing containers.
  10. The sample HelloWorldProvider will be displayed in a Table Desktop layout. To add the channel in the JSPTableContainer from the administration console, follow instructions in section "To Add a Channel or Container Channel" in Chapter 5 of the Sun ONE Portal Server 6.0 Administrator's Guide. To add the channel in the JSPTableContainer manually:

    1. Add the HelloWorldProvider channel XML fragment (in the HelloContainerPPA1.xml file) for the JSPTableContainer as shown in Code Example 2-13.


    2. Code Example 2-13    HelloContainerPPA1.xml File

      <?xml version="1.0" encoding="utf-8" standalone="no"?>
      <!DOCTYPE DisplayProfile SYSTEM "jar://resources/psdp.dtd">

      <Container name="JSPTableContainer" provider="JSPTableContainerProvider">
          <Properties>
              <Collection name="Personal Channels">
                  <String value="HelloWorldPPA1"/>
              </Collection>
              <Collection name="channelsRow">
                  <String name="HelloWorldPPA1" value="3"/>
              </Collection>
          </Properties>
          <Available>
              <Reference value="HelloWorldPPA1"/>
          </Available>
          <Selected>
              <Reference value="HelloWorldPPA1"/>
          </Selected>
          <Channels>
          </Channels>
      </Container>

    3. Use the dpadmin add subcommand to add the channel to a container. If you do not specify a parent object with the -p option, the channel is added at the root level.
    4. dpadmin modify -m -p "JSPTableContainer" -u dn_amadmin -w password -d distinguishedname HelloContainerPPA1.xml

  11. Create the template files if the new provider requires template files.
  12. The sample HelloWorldProvider requires content.template and edit.template files.

    1. Create the content.template file for the HelloWorldProvider.
    2. The contents of content.template file is shown in Code Example 2-14.



      Code Example 2-14    HelloWorldProviderPPA1 content.template File

      <head></head>
      <body bgcolor="white">
      [tag:welcome]<br><br>
      [tag:message]<br><br>
      [tag:properties]
      </body>
      </html>

    3. Create the edit.template file for the HelloWorldProvider.
    4. The contents of edit.template file is shown in Code Example 2-15.



      Code Example 2-15    HelloWorldProviderPPA1 editTemplate.template File

      <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
      <HTML>
          <HEAD>
              [tag:noCache]
              <TITLE>[tag:productName]</TITLE>
          </HEAD>
          <BODY BGCOLOR="#FFFFFF">
              <FONT SIZE=+0 FACE="[tag:fontFace1]">
              <LINK REL="stylesheet" TYPE="text/css" HREF="[surl:/desktop/css/style.css]" TITLE="fonts">
              <CENTER>

              [tag:bulletColor]
              [tag:banner]

              <FORM ACTION="dt" NAME="edit_form" METHOD=POST ENCTYPE="application/x-www-form-urlencoded">
              <INPUT TYPE=HIDDEN NAME="action" SIZE=-1 VALUE="process">
              <INPUT TYPE=HIDDEN NAME="provider" SIZE=-1 VALUE="[tag:providerName]">

              [tag:inlineError]

              <TABLE BORDER=0 CELLPADDING=0 CELLSPACING=3 WIDTH="100%">
                  <TR>
                      <TD WIDTH="100%" VALIGN=TOP>
                          <CENTER>
                              <BR>
                              <FONT SIZE="+2" FACE="[tag:fontFace1]">
                              <B>
                              Edit [tag:title]</B></FONT>
                              <BR>
                              [tag:contentOptions]
                          </CENTER>
                      </TD>
                      </TR>
              </TABLE>

              <BR>

              <FONT SIZE=+0 FACE="[tag:fontFace1]">
              <INPUT TYPE=SUBMIT NAME="Submit" VALUE="Finished" CLASS="button">
              <INPUT TYPE=BUTTON OnClick="location='[tag:desktop_url]'" VALUE="Cancel" CLASS="button">
              </font>

              <br>

              <P>
              </FORM>
              <BR>
              [tag:menubar]
              </font>
          </BODY>
      </HTML>

  13. Create the channel directory under the template root directory.
  14. By default, the template root directory is /etc/opt/SUNWps/desktop/default. For this example, create a HelloWorldPPA1 directory under /etc/opt/SUNWps/desktop/default directory.

  15. Copy all the template files over to the newly created directory (in Step 7). For example:
  16.    cp content.template /etc/opt/SUNWps/desktop/default/HelloWorldPPA1

       cp edit.template /etc/opt/SUNWps/desktop/default/HelloWorldPPA1

  17. Create a resource bundle properties file.
  18. The sample HelloWorldProvider includes a HelloWorldProviderPPA1.properties file.

    1. Create a HelloWorldProviderPPA1.properties properties file.
    2.    Include the following property in the file:
    3. welcome=Welcome to Hello World!!

  19. Copy the resource bundle to the user defined class directory.
  20. By default, the class directory to copy the resource bundle into is /etc/opt/SUNWps/desktop/classes.

  21. Access the HelloWorld channel inside the JSPTableContainer. To access, type the following URL in your browser:
  22. http://hostname:port/portal/dt?provider=JSPTableContainer

Example 2

This section provides the instructions for creating a custom provider by extending the ProfileProviderAdapter. The process includes creating a sample provider that prints "Hello World!" on the provider's channel. The following sample HelloWorldProvider reads a string property from the user's display profile and allows the user to edit the string.

The sample HelloWorldProvider described here will also support EDIT_COMPLETE, which means this provider is responsible for generating the complete edit form with header, footer, and handling of the form submit actions. To accomplish this:

  1. Create the Java class, which will generate the content for the channels backed by this provider.
  2. For the sample HelloWorldProvider, create the class file as shown in Code Example 2-16.



    Code Example 2-16    HelloWorldProviderPPA2.java File

    package custom.helloworld;

    import java.util.Hashtable;
    import java.util.ResourceBundle;

    import java.net.URL;

    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;

    import com.sun.portal.providers.ProfileProviderAdapter;
    import com.sun.portal.providers.ProviderException;
    import com.sun.portal.providers.context.ProviderContext;
    import com.sun.portal.providers.context.ProviderContextException;
    import com.sun.portal.providers.context.Theme;

    public class HelloWorldProviderPPA2 extends ProfileProviderAdapter {
        private ResourceBundle bundle = null;
        private final static String BANNER = "banner";
        private final static String BANNER_TEMPLATE = "banner.template"
        private final static String BORDER_COLOR = "borderColor";
        private final static String BORDER_WIDTH = "borderWidth";
        private final static String BULLET_COLOR = "bulletColor";
        private final static String BULLET_COLOR_JS = "bulletColor.js";
        private final static String BG_COLOR = "bgColor";
        private final static String CONTENT_LAYOUT = "contentLayout";
        private final static String CONTENT_LAYOUT_TEMPLATE = "contentLayout.template";
        private final static String DESKTOP_URL = "desktop_url";
        private final static String EDIT_PROVIDER_TEMPLATE = "editTemplate.template";
        private final static String ERR_MESSAGE = "errMessage";
        private final static String FONT_COLOR = "fontColor";
        private final static String FONT_FACE = "fontFace";
        private final static String FONT_FACE1 = "fontFace1";
        private final static String FRONT_CONTAINER_NAME = "frontContainerName";
        private final static String INLINE_ERROR = "inlineError";
        private final static String MENUBAR = "menubar";
        private final static String MENUBAR_TEMPLATE = "menubar.template";
        private final static String MESSAGE = "message";
        private final static String NO_CACHE = "noCache";
        private final static String NO_CACHE_TEMPLATE = "noCache.template";
        private final static String PARENT_CONTAINER_NAME = "parentContainerName";
        private final static String PRODUCT_NAME = "productName";
        private final static String THEME_CHANNEL = "theme_channel";
        private final static String TITLE_BAR_COLOR = "titleBarColor";
        private final static String TOOLBAR_ROLLOVER = "toolbarRollover";
        private final static String TOOLBAR_ROLLOVER_JS = "toolbarRollovers.js";
        /*Implement the getContent method*/
         public StringBuffer getContent(
         HttpServletRequest req, HttpServletResponse res
         ) throws ProviderException {
            Hashtable tags = getStandardTags(req);
            if (bundle == null) {
                 bundle = getResourceBundle();
            } tags.put("welcome", bundle.getString("welcome"));
            tags.put("message", getStringProperty("message"));
            StringBuffer b = getTemplate("content.template", tags);
            return b;
        }

        /*Implement the getEdit method*/
        public StringBuffer getEdit(
        HttpServletRequest req, HttpServletResponse res)
        throws ProviderException {
            String containerName = req.getParameter("containerName");
            String providerName = req.getParameter("targetprovider");
            String editChannelName = req.getParameter("provider");
            // Get the standard tags, which will include the menubar, footer, etc.
            Hashtable tags = getStandardTags(req);
            tags.put("help_link", getHelpLink("editPage",req));
            // Provider specific tags
            tags.put(MESSAGE, getStringProperty(MESSAGE));
            tags.put("title", getTitle());
            tags.put( "providerName", providerName);
            tags.put( "contentOptions", getTemplate("edit.template", tags).toString());
            if (containerName != null) {
                 tags.put(FRONT_CONTAINER_NAME, containerName);
            }
             StringBuffer b = getTemplate(EDIT_PROVIDER_TEMPLATE, tags);;
             return b;
        }

        /* Implement Process Edit Method */
        public URL processEdit(
        HttpServletRequest req, HttpServletResponse res )
        ) throws ProviderException {
            String message = req.getParameter(MESSAGE);
            if(message != null) {
                if( !message.equals(getStringProperty(MESSAGE)) ) {
                    setStringProperty(MESSAGE,message);
                    return null;
                }
            } else {
                 setStringProperty(MESSAGE,"");
            }
            return null;
        }
        /* HelloWorld Provider supports, edit_type=edit_complete which means, the provider is responsible for generating a complete edit form with header,footer and html buttons. getStandardTags method populates the hashtable with all the standard tags */
        protected Hashtable getStandardTags(HttpServletRequest req) throws ProviderException {
            Hashtable tags = new Hashtable();
            ProviderContext pc = getProviderContext();
            String property = null;
            property = FONT_FACE1;
            tags.put(FONT_FACE1, getStringProperty(FONT_FACE1));
            property = PRODUCT_NAME;
            tags.put(PRODUCT_NAME, getStringProperty(PRODUCT_NAME));
            tags.put(PARENT_CONTAINER_NAME, pc.getDefaultChannelName());
            // templates
            tags.put(MENUBAR, getTemplate(MENUBAR_TEMPLATE));
            tags.put(CONTENT_LAYOUT, getTemplate(CONTENT_LAYOUT_TEMPLATE));
            tags.put(TOOLBAR_ROLLOVER, getTemplate(TOOLBAR_ROLLOVER_JS));
            tags.put(NO_CACHE, getTemplate(NO_CACHE_TEMPLATE));
            tags.put(DESKTOP_URL, pc.getDesktopURL(req));
            tags.put(BULLET_COLOR, getTemplate(BULLET_COLOR_JS));
             tags.put(BANNER, getTemplate(BANNER_TEMPLATE));
            // error tags (if any)
             String err = req.getParameter("error");
             if (err != null) {
                 tags.put(ERR_MESSAGE, err);
                 tags.put("inlineError", getTemplate("inlineError.template"));
             } else {
                 // no error, put in dummy tags so lookup doesn't fail
                 tags.put(ERR_MESSAGE, "");
                 tags.put(INLINE_ERROR, "");
            }
            // theme attributes
                try {
                    tags.put( BG_COLOR, Theme.getAttribute( getName(), pc, BG_COLOR ));
                    tags.put( TITLE_BAR_COLOR, Theme.getAttribute( getName(), pc, TITLE_BAR_COLOR ));
                    tags.put( BORDER_COLOR, Theme.getAttribute( getName(), pc, BORDER_COLOR ));
                    tags.put( FONT_COLOR, Theme.getAttribute( getName(), pc, FONT_COLOR ));
                    tags.put( BORDER_WIDTH, Theme.getAttribute( getName(), pc, BORDER_WIDTH ));
                    tags.put( FONT_FACE, Theme.getAttribute( getName(), pc, FONT_FACE));
                    if( Theme.getSelectedName(getName(), pc).equals( Theme.CUSTOM_THEME ) ) {
                        tags.put(THEME_CHANNEL, getStringProperty("customThemeChannel"));
                    } else {
                        tags.put(THEME_CHANNEL, getStringProperty("presetThemeChannel"));
                    }
                } catch (ProviderContextException pce) {
                    throw new ProviderException( "TemplateContainerProvider.getStardardTags(): failed to obtain theme related attribute ", pce );
                }
                 return tags;
            }
            /* GetHelpLink for the Help button in the banner and footer of the edit page */
            protected String getHelpLink(String key, HttpServletRequest req) throws ProviderException{
                try {
                    URL helpLink = getHelp(req, key);
                    if (helpLink != null) {
                        return helpLink.toString();
                    }
                    else {
                        return "";
                }
            } catch (Throwable t ) {
                throw new ProviderException("HelloWorldProvider.getHelpLink(): could not get help URL for " + key, t);
            }
        }
    }

  3. Compile the HelloWorldProviderPPA2.java class file. To compile the HelloWorldProviderPPA2.java file, type:
  4. javac -d /etc/opt/SUNWps/desktop/classes -classpath BaseDir/SUNWps/sdk/desktop/desktopsdk.jar:BaseDir/SUNWam/lib/servlet.jar HelloWorldProviderPPA2.java

  5. Create a directory structure which reflects the packaging of this provider under the user defined class directory.
  6. By default, provider class files are stored in /etc/opt/SUNWps/desktop/classes directory. For example:

    1. Change directories to /etc/opt/SUNWps/desktop/classes.
    2. Type mkdir custom/helloworld.

  7. Copy HellowWorldProviderPPA2.class into /etc/opt/SUNWps/desktop/classes/custom/helloworld directory.
  8. Create the resource bundle properties file for the HelloWorldProvider (HelloWorldProviderPPA2.properties) and include the following string:
  9.    welcome=Welcome to Hello World!!

  10. Copy the resource bundle properties file, HelloWorldProviderPPA2.properties, into /etc/opt/SUNWps/desktop/classes directory.
  11. Develop the template files for the HelloWorldProvider.
  12. The HelloWorldProvider requires the following template files:

    • content.template (see Code Example 2-17)
    • edit.template (see Code Example 2-18) - This file generates the editable fields in the form
    • editTemplate.template (see Code Example 2-19) - This file generates a complete form


    • Code Example 2-17    HelloWorldProviderPPA2 content.template File

      <head></head>
      <body bgcolor="white">
          [tag:welcome]<br><br>
          [tag:message]<br><br>
      </body>
      </html>



      Code Example 2-18    HelloWorldProviderPPA2 edit.template File

      <p>
      <table border="0" cellpadding="2" cellspacing="0" width="100%">

          <tr>
              <td colspan="3" bgcolor="#333366">
                  <font size=+1 face="[tag:fontFace1]" color="#FFFFFF"><b>Welcome</b></font>
              </td>
          </tr>

          <tr><td><br><br><br></td></tr>

          <tr>
              <td width="20%" align="RIGHT" NOWRAP>
                  <font face="[tag:fontFace1]" color="#000000"><label for="message"><b>Greeting:</b></label></font>
              </td>
              <td width="45%">
                  <font face="[tag:fontFace1]" size="+0">
                      <input type="TEXT" name="message" size="50" maxlength="50" value="[tag:message]" id="message">
                  </font>
              </td>
          </tr>

          <tr>
              <td width="20%">&nbsp;</td>
              <td width="45%">&nbsp;</td>
          </tr>

      </table>



      Code Example 2-19    HelloWorldProviderPPA2 editTemplate.template File

      <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2//EN">
      <HTML>
          <HEAD>
              [tag:noCache]
              <TITLE>[tag:productName]</TITLE>
          </HEAD>
          <BODY BGCOLOR="#FFFFFF">
              <FONT SIZE=+0 FACE="[tag:fontFace1]">
              <LINK REL="stylesheet" TYPE="text/css" HREF="[surl:/desktop/css/style.css]" TITLE="fonts">
              <CENTER>

              [tag:bulletColor]
              [tag:banner]

              <FORM ACTION="dt" NAME="edit_form" METHOD=POST ENCTYPE="application/x-www-form-urlencoded">
              <INPUT TYPE=HIDDEN NAME="action" SIZE=-1 VALUE="process">
              <INPUT TYPE=HIDDEN NAME="provider" SIZE=-1 VALUE="[tag:providerName]">

              [tag:inlineError]

              <TABLE BORDER=0 CELLPADDING=0 CELLSPACING=3 WIDTH="100%">
                  <TR>
                      <TD WIDTH="100%" VALIGN=TOP>
                          <CENTER>
                              <BR>
                              <FONT SIZE="+2" FACE="[tag:fontFace1]">
                              <B>
                              Edit [tag:title]</B></FONT>
                              <BR>
                              [tag:contentOptions]
                          </CENTER>
                      </TD>
                      </TR>
              </TABLE>

              <BR>

              <FONT SIZE=+0 FACE="[tag:fontFace1]">
              <INPUT TYPE=SUBMIT NAME="Submit" VALUE="Finished" CLASS="button">
              <INPUT TYPE=BUTTON OnClick="location='[tag:desktop_url]'" VALUE="Cancel" CLASS="button">
              </font>

              <br>

              <P>
              </FORM>
              <BR>
              [tag:menubar]
              </font>
          </BODY>
      </HTML>

  13. Copy the templates for the provider into the channel directory under the templates root directory.
  14. By default, templates are stored in /etc/opt/SUNWps/desktop/default directory.

  15. Create the HelloWorldProvider provider and channel definition for the display profile.
  16. The HelloWorldProvider provider display profile XML fragment (in file HelloProviderPPA2.xml) is shown in Code Example 2-20.



    Code Example 2-20    HelloProviderPPA2.xml File

    <?xml version="1.0" encoding="utf-8" standalone="no"?>
    <!DOCTYPE DisplayProfile SYSTEM "jar://resources/psdp.dtd">

    <Provider name="HelloWorldProviderPPA2" class="custom.helloworld.HelloWorldProviderPPA2">
        <Properties>
            <String name="title" value="*** TITLE ***"/>
            <String name="presetThemeChannel" value="JSPPresetThemeContainer" advanced="true"/>
            <String name="customThemeChannel" value="JSPCustomThemeContainer" advanced="true"/>
            <String name="description" value="*** DESCRIPTION ***"/>
            <String name="refreshTime" value="0"/>
            <Boolean name="isEditable" value="true"/>
            <String name="editType" value="edit_subset"/>
            <Collection name="aList">
                <String value="i'm aList"/>
            </Collection>
            <Collection name="aMap">
                <String name="title" value="Ramag"/>
                <String name="desc" value="My Map"/>
                <Boolean name="removable" value="true"/>
                <Boolean name="renamable" value="true"/>
            </Collection>
            <String name="message" value="Hello World Test Provider"/>
        </Properties>
    </Provider>

    The HelloWorldProvider channel display profile XML fragment (in file HelloChannelPPA2.xml) is shown in Code Example 2-21.



    Code Example 2-21    HelloChannelPPA2.xml File

    <?xml version="1.0" encoding="utf-8" standalone="no"?>
    <!DOCTYPE DisplayProfile SYSTEM "jar://resources/psdp.dtd">

    <Channel name="HelloWorldPPA2" provider="HelloWorldProviderPPA2">
        <Properties>
            <String name="title" value="Hello World Channel"/>
            <String name="fontFace1" value="sans-serif"/>
            <String name="productName" value="Sun ONE Portal Server"/>
            <String name="editType" value="edit_complete"/>
            <String name="description">This is a test of the hello world provider</String>
            <String name="width" value="thin"/>
            <String name="message" value="Hello World Test!!! - non-localized "/>
            <Locale language="en" country="US">
                <String name="message" value="Hello World - I am speaking English in the United States!!!"/>
            </Locale>
        </Properties>
    </Channel>

  17. Load the HelloWorldProvider provider and channel definitions using the dpadmin command. That is, type:
  18. /opt/SUNWps/bin/dpadmin add -u dn_amadmin -w password -d distinguishedname HelloProviderPPA2.xml

    /opt/SUNWps/bin/dpadmin add -u dn_amadmin -w password -d distinguishedname HelloChannelPPA2.xml



    Note

    The dpadmin subcommand add will work only if the HelloWorldProvider does not exist at the time of executing the command. If a HelloWorldProvider exists, use the modify subcommand to update the display profile fragment for the provider. See the Sun ONE Portal Server 6.0 Administrator's Guide for more information on the dpadmin tool.



  19. Access the channel from a browser. To access, type the following URL in your browser:
  20. http://hostname:port/portal/dt?provider=HelloWorldPPA2

To display the provider's channel as one of the leaf channels for a container, you have to add the channel into the container's available and selected list. To accomplish this:

  1. Add it to the Available and Selected list for the container. For example, to add the HelloWorldProvider to the TemplateTableContainer, type the following:
    • To add to the Available list:
    •    dpadmin modify -m -p "TemplateTableContainer" -u dn_amadmin -w password -d distinguishedname << EOF

         <?xml version="1.0" encoding="utf-8" standalone="no"?>

         <!DOCTYPE DisplayProfile SYSTEM "jar://resources/psdp.dtd">

         <Available>

            <Reference value="HelloWorldPPA2"/>

         </Available>

         EOF

    • To add to the Selected list:
    •    dpadmin modify -m -p "TemplateTableContainer" -u dn_amadmin -w password -d distinguishednode << EOF

         <?xml version="1.0" encoding="utf-8" standalone="no"?>

         <!DOCTYPE DisplayProfile SYSTEM "jar://resources/psdp.dtd">

         <Selected>

            <Reference value="HelloWorldPPA2"/>

         </Selected>

         EOF

  2. Access the HelloWorld Channel inside the TemplateTableContainer. To access, type the following URL in your browser:
  3. http://hostname:port/portal/dt?provider=TemplateTableContainer

Utilize the par utility for transporting channels and providers. For example, the following command can be used to export the HelloWorld channel and provider into a par file which can be imported to another Sun ONE Portal Server system.

par export -r dn_amadmin -p password -v helloworld.par distinguishednode helloworld.txt

Here, helloworld.txt contains the following:



Code Example 2-22    HelloWorldProvider Sample helloworld.txt File

from: channel+provider HelloWorld
file: providerClassBaseDir HelloWorldProvider.properties provider
class: custom.helloworld.HelloWorldProvider
directory: templateBaseDir . HelloWorld
desc: HelloWorld Provider

Using the following command, the contents of the helloworld.par can be imported to as HelloWorld channel in a different Sun ONE Portal Server installation.

par import -r dn_amadmin -p password -v helloworld.par distinguishednode provider=HelloWorldProvider,channel=HelloWorld,avail=TemplateTableC ontainer,selected

The above command, apart from installing the channel and provider, will also include the HelloWorld channel as one of the selected and available channels in the TemplateTableContainer. For more information on the par utility and its subcommands, see the Sun ONE Portal Server 6.0 Administrator's Guide.


Previous      Contents      Index      Next     
Copyright 2002 Sun Microsystems, Inc. All rights reserved.