Oracle9iAS Portal

 

Building a Portlet for Microsoft Exchange

Integrating Applications with Oracle9iAS Portal

October 2001

Oracle9iAS

Contents

A portal is a Web page that aggregates data from other sources.

Overview

This article describes a sample portlet provider that accesses data from Microsoft Exchange, a messaging and collaboration application. Oracle9iAS Portal integrates this data and displays it in a Web page.

Oracle9iAS Portal provides three Portal Developer Kits: PDK-Java, PDK-PL/SQL, and PDK-URL Services. The Exchange portlet was built using the PDK-Java.

If you're new to Oracle9iAS Portal, portlets, or the PDK-Java, the article Building Portlets with JavaBeans: Developer's Overview can help you come up to speed.

Download

Exchange 2000 portlet Exchange provider for Microsoft Exchange 2000

The download includes an Oracle JDeveloper project that makes it easy to view the code and build the provider.

Architecture

A portal is a Web page that aggregates data from other sources. Increasingly, developers are using portal software to offer end-users consolidated, personalized views of applications and information. Oracle9iAS Portal pages display content in regions called portlets, but the portal doesn't communicate with portlets directly. Instead, a software entity called a provider acts as a conduit between the portal, one or more portlets, and an underlying data source, Web page, or application. A sample provider called the Exchange provider interacts with Microsoft Exchange to supply data to these portlets:

  • Inbox displays messages from the user's Microsoft Exchange inbox.
  • Calendar displays the user's appointments and meetings.
  • Contacts displays the user's list of contacts.

The Exchange provider supplies data to three portlets. The Inbox portlet displays messages from the user's Microsoft Exchange inbox. The Calendar portlet displays the user's appointments and meetings. The Contacts portlet displays the user's list of contacts.

Each user's messages, appointments, and contacts reside on the Exchange server. The provider interacts with the Exchange server via ASPs (Active Server Pages) stored and executed on IIS (Microsoft Internet Information Server). The ASPs query the Exchange server and return results in HTML. The Exchange provider invokes specific ASPs via HTTP or HTTPS to generate the HTML for each portlet. Each portlet also enables the user to create and edit individual items (messages, appointments, or contacts) via HTML links to the Microsoft Outlook Web Access application.

The Exchange server stores each user's messages, appointments and contacts data. Each portlet provides HTML links to the Microsoft Outlook Web Access application. The IIS server hosts the ASPs that generate the HTML content displayed in the portlets. Oracle9iAS Portal pages display content in regions called portlets. Inbox displays messages from the user's Microsoft Exchange inbox. Calendar displays the user's appointments and meetings. Contacts displays the user's list of contacts. A provider acts as a conduit between the portal, one or more portlets, and the underlying data source. Portal users can invoke Outlook Web Access to create and edit items.

The PDK framework provides most of what a portlet provider needs to access an external application like Microsoft Exchange—there's relatively little code to write. The key tasks are installing and registering the external application. The Help files that come with the Exchange providers (Exchange 5.5 and Exchange 2000) include detailed instructions for administrators and users.

The Exchange provider is based on the Flights of Fancy example included with the PDK-Java and documented on OTN. For demonstration purposes, Flights of Fancy was built as a stand-alone Java servlet and then converted to run as an Oracle9iAS Portal Provider. Application logic was distributed among several classes to make the conversion as simple as possible. The Exchange provider example follows a similar pattern; it's a converted servlet, and these are the main classes:

Class Description
ExternalServlet Receives requests from the HTTP listener and routes them to the ExchangeProvider class.
ExternalProvider Implements provider functionalilty, that is, it handles interaction between the portal, the portlets, and the external Exchange application. ExternalProvider extends DefaultProvider, the PDK-Java's base implementation of the Provider interface. When the DefaultProvider is instantiated, it processes a file named provider.xml that describes the provider and its portlets. DefaultProvider creates an instance of each portlet described in the file, then pushes the corresponding meta and configuration data. A provider also exposes functions that are shared by the portlets it manages, typically via a session created and maintained by the provider. For example, ExternalProvider overrides the initSession method to process login cookies for the portlets.
ExchangeProvider This class isn't really a provider—the name was chosen to indicate its central role in the Exchange provider architecture. Its main jobs are processing messages from ExternalServlet and responding to portlet rendering requests.
ApplicationLogin Deals with user login and inspecting requests to find out if the user is logged in.

As part of the conversion, classes were added to render the various portlets (example: ExchangeInboxRenderer), and to enable end-users to personalize the portlets (example: InboxPersonalization). All of these classes are packaged in a Java archive file named exchangeProvider.jar, which, when deployed, comprises the Exchange provider.

The rest of this article describes how specific Exchange provider features were implemented.

Authenticating Users

Oracle9iAS Portal enables access to two kinds of applications, each with different authentication schemes:

  • Partner applications are integrated with the Oracle9iAS Single Sign-On Server. They call on the Oracle9iAS Single Sign-On API to accept a user's identity as validated by the Single Sign-On Server. Typical partner applications are developed to run only in the context of a portal.
  • External applications do not delegate responsibility for authenticating users to the Single Sign-On Server. They are developed to run independently and manage their own login data, but a Single Sign-On Server account can be mapped to several external application usernames and passwords. When a user tries to access a portlet based on an external application, the Single Sign-On Server automatically sends the user's credentials to the appropriate external application.

In either case, the Oracle9iAS Portal provider framework uses constructs called cookies to manage login credentials. A cookie (the name was chosen by Netscape engineers "for no compelling reason") stores a small amount of text sent by a server to a Web browser. The login cookie expires with the session, either at the end of a time interval specified by the adminstrator or when the user exits the browser. It is never written to disk.

The Exchange provider is an external application provider. As such, it allows single sign-on access to a Microsoft Exchange mailbox. A user logging in to the portal for the first time must also log in to the Exchange external application. These credentials are stored in the Single Sign-On Server and are sent to Exchange each time the user views the Exchange portlets.

When the Exchange provider is initialized, ExternalProvider.initSession gets the user's Exchange credentials from the login server, verifies them, and sets the login cookie as shown in the following listing.

public class ExternalProvider extends DefaultProvider
...
  public Object[] initSession(ProviderUser user, 
                              ExternalPrincipal extuser) 
                              throws ProviderException,
AuthenticationException
{
ApplicationLogin login = new ApplicationLogin();
Cookie appCookie = null;
Cookie[] cookies = new Cookie[1];
System.err.println("IN ExternalProvider.initSession...");
System.err.println(" App user: "+extuser.toString());
if ((appCookie = login.getApplicationCookie(extuser)) == null)
{
System.err.println(" User DOES NOT have credentials.");
throw new AuthenticationException("Login failed.");
}
// Login was successful, so establish PDK session
super.initSession(user, extuser);
System.err.println(" User DOES have credentials.");
System.err.println(" After setting up Jserv session cookie");
appCookie.setMaxAge(1800);
System.err.println(" After setting expiry");
cookies[0] = appCookie;
System.err.println(" Returning cookies");
return cookies;
} ... }

Note: The Exchange provider is a sample application, not a shipping product, so the source code contains statements that display information about application status, user credentials, etc. Such statements are useful for debugging and learning, but they should be removed if you want to keep the corresponding information private.

The listing below shows more code from ExchangeProvider.process. If the user has already logged in and a login cookie exists, this code calls showPortlet to display the portlet's data. Otherwise, the provider framework handles the exception and prompts the user to login.

public class ExchangeProvider
...
  public void process(PortletRenderRequest pr, String mode)
throws Exception
{
mPR = pr;
mIsWebProvider = true;
ApplicationLogin login = new ApplicationLogin(pr);
try
{
...
if (login.isLoggedIn())
{
user = login.getUser();
upwd = login.getPassword();
udomain = login.getDomain();
umbox = login.getMailbox();
uExServer = login.getExServer();
uWebServerURL = login.getWebServerURL();
uOutlookWebAccessURL = login.getOutlookWebAccessURL();
showPortlet(mode, user, upwd, udomain, umbox, uExServer, uWebServerURL, uOutlookWebAccessURL, server, pr.getWriter("text/html"), pr);
}//if logged in
}
catch (Exception e)
{
throw e;
}
} ... }

Note: Each portlet gives users the option to create and edit individual items (messages, appointments, contacts) via HTML links to the Microsoft Outlook Web Access application. However, that application is not registered with Oracle9iAS Portal or the Single Sign-On Server. Consequently, a user must login to Outlook Web Access once per browser session.

Invoking the Application

The Exchange provider supplies data from the Exchange server to the Inbox, Calendar, and Contacts portlets. The provider interacts with the Exchange server via ASPs (Active Server Pages, a Microsoft-proprietary technology similar to JavaServer Pages) written by the provider developers and stored and executed on IIS (Microsoft Internet Information Server).

The following code from ExchangeProvider.showPortlet shows how the provider gets a user's Inbox data from the Exchange server by invoking inbox.asp, then outputs the results.

public class ExchangeProvider
...
  private void showPortlet(...) throws Exception {

    ...
    HTTPResponse rsp;
        ...

        HTTPConnection con = new HTTPConnection(url);
        ...

            NVPair form_data[] = new NVPair[13];
form_data[0] = new NVPair("uname", user);
... form_data[12] = new NVPair("upwd", password);
rsp = con.Get(virtDir + "inbox.asp", form_data);

... inStream = new BufferedReader( new InputStreamReader(rsp.getInputStream())
);
while ((line = inStream.readLine())!= null) {
out.println(line);
}
} ... }

Personalizing the Portlets

Oracle9iAS Portal enables end-users to personalize portlets by filling out HTML forms. These same features are available to developers through the PDK-Java. There are two main tasks: back-end data management and front-end user interface management.

On the back end, personalization data is represented by a set of name-value pairs (example: "ROWS_PER_PAGE", "10"). The PDK-Java includes a NameValuePersonalizationObject class that implements the PersonalizationObject interface to hold arbitrary parameters as name-value pairs and manage their persistence. The Exchange provider includes classes that extend NameValuePersonalizationObject to handle parameters specific to each portlet. For example, the following code from InboxPersonalization.init initializes personalization parameters for the Inbox portlet.

public class InboxPersonalization 
       extends NameValuePersonalizationObject
{ ... public void init(PortletReference ref)
{
super.init(ref);
this.setPortletTitle("Exchange Inbox Portlet");
this.putInteger("CACHE_DURATION", new Integer(10));
this.putString("PAGE", "1");
this.putString("ROWS_PER_PAGE", "10");
this.putString("SENDER_FILTER", "");
this.putString("SUBJECT_FILTER", "");
this.putString("START_DATE_FILTER", "");
this.putString("END_DATE_FILTER", "");
} ... }

Personalization data is stored and retrieved by PortletPersonalizationManager objects. The PDK-Java framework uses the abstraction of a PersonalizationObject as a 'container' for a set of personalization parameters and a PortletReference as the 'key' under which the values are stored. Thus, a PortletPersonalizationManager allows the storage and retrieval of persisted PersonalizationObjects under a given PortletReference. The PDK-Java currently provides two PortletPersonalizationManager implementations:

  • FilePersonalizationManager - a lightweight implementation that stores data in a file system.
  • DBPersonalizationManager - an implementation that stores data in a relational database.

The provider developer specifies a PortletPersonalizationManager and a PersonalizationObject for each portlet via tags in the provider.xml file. For example, the listing below highlights the relevant tags from provider.xml for the Exchange provider's Inbox portlet. All of the Exchange portlets use the FilePersonalizationManager, and each portlet has its own PersonalizationObject (InboxPersonalization, CalendarPersonalization, and ContactsPersonalization). This is essentially all a provider developer needs to do to manage back-end personalization data—the PDK-Java framework takes care of the rest.

...
<portlet class="oracle.portal.provider.v1.http.DefaultPortlet" version="1" >
<id>1</id>
<name>ExchangeInboxPortlet</name> ... <portletRenderer class="oracle.exchange.application.ExchangeInboxRenderer" /> <portletPersonalizationManager class="oracle.portal.provider.v1.FilePersonalizationManager">
<dataClass>oracle.exchange.application.InboxPersonalization</dataClass>
</portletPersonalizationManager>
</portlet> ...

To handle front-end user interface requirements, PDK-Java provides classes called PortletRenderers. A PortletRenderer does the following tasks to support the customization process:HTML form for personalizing the Inbox portlet. Apply OK Cancel In the Exchange Inbox portlet, you can customize the portlet title and the cache expiration period. You can also personalize this portlet's view of your inbox by specifying the number of message headers to display and by filtering messages. Messages can be filtered by sender, subject, and date received. Standard header Standard footer Exchange Inbox Messages 10 10 minutes (use mm/dd/yy) (use mm/dd/yy) Title Cache Expires Every Rows Per Page Sender Contains Subject Contains Received On or After Received Before

  • Displays the 'edit form': PortletRenderer uses a PortletPersonalizationManager to retrieve personalization parameter values and renders controls in an HTML form so an end-user can edit them.
  • Handles edit form actions: When an end-user clicks OK or Apply in the standard edit form header, PortletRenderer uses a PortletPersonalizationManager to store the values submitted by the edit form and redirects the browser to the appropriate Portal page.

The PDK-Java also provides utility methods (examples: PortletRendererUtil.renderCustomizeFormHeader and PortletRendererUtil.renderCustomizeFormFooter) that make it easy to add standard elements (such as headers and footers) to the forms.

The following code from ExchangeInboxRenderer.renderEdit shows how to display the edit form and respond to user actions.

public class ExchangeInboxRenderer implements PortletRenderer{
...
  private void renderEdit(PortletRenderRequest pr) 
                 throws PortletException, AccessControlException
{
try {
PrintWriter out = pr.getWriter("text/html");
String action = pr.getParameter("inbox_action");
ProviderSession session = pr.getSession();
InboxPersonalization data = (InboxPersonalization)PortletRendererUtil.getEditData(pr);
if (action != null) {
data.setPortletTitle(pr.getParameter("inbox_title"));
data.putString("PAGE", "1"); // reset to page 1
data.putInteger("CACHE_DURATION", new Integer(pr.getParameter("inbox_cache")));
data.putString("ROWS_PER_PAGE", pr.getParameter("inbox_num_rows"));
...
PortletRendererUtil.submitEditData(pr, data);
... if (action.equalsIgnoreCase("OK")) {
HttpPortletRendererUtil.sendRedirect(pr, pr.getBackURL());
return;
}
else if (action.equalsIgnoreCase("APPLY")) {
HttpPortletRendererUtil.sendRedirect(pr, pr.getPageURL());
return;
}
}
else {
PortletRendererUtil.renderCustomizeFormHeader(pr,out,null, "inbox_action",null,null);
... out.println("<table><tr><td>");
out.println("<table CELLSPACING=3>");
... out.println("<tr><td>Cache Expires Every:</td><td><INPUT TYPE=\"text\" "+
"SIZE=\"40\" MAXLENGTH=\"3\" NAME=\"inbox_cache" + "\" VALUE=\""
+ data.getInteger("CACHE_DURATION") + "\" onBlur=\"isInteger(this)\" /> minutes</td></tr>");

out.println("<tr><td>Rows Per Page:</td><td><INPUT TYPE=\"text\" "+
"SIZE=\"40\" MAXLENGTH=\"3\" NAME=\"inbox_num_rows" + "\" VALUE=\""
+ data.getString("ROWS_PER_PAGE") + "\" onBlur=\"isInteger(this)\" /></td></tr>");
... out.println("</table>");
PortletRendererUtil.renderCustomizeFormFooter(pr, out);
}
}
catch (Exception e)
{
}
} ...

Caching Data to Improve Performance

Each portlet also features data caching. The user can set the number of minutes between each cache expiration in the portlet's "Customize" page. If the user clicks on "Next" or "Previous" or commits customization changes, the cache refreshes. Cached data can improve performance of the portal because the provider doesn't have to fetch it from the server in response to each request. At the same time, this implementation also lets the user choose to display the latest data at will.

The following code from ExchangeInboxRenderer.renderShow shows how the provider works with the cache to get the appropriate version of the portlet content. The code calls several methods from the HttpPortletRendererUtil class. This class, which is part of the PDK-Java, provides many useful methods, including utilities that access either the validation or expiry-based caching provided by the portal. Also, HttpPortletRendererUtil extends PortletRendererUtil, implementing those utilities that depend on HTTP servlet interfaces.

public class ExchangeInboxRenderer implements PortletRenderer{
...

    private void renderShow(PortletRenderRequest pr) 
           throws PortletException, AccessControlException
{
try
{
InboxPersonalization data = (InboxPersonalization)PortletRendererUtil.getEditData(pr);
PrintWriter out = pr.getWriter("text/html");
// Validate Cache
ProviderSession session = pr.getSession();
boolean invalid = false;
long currentTime = new Date().getTime();
// invalidate the cache if the "hasChanged" parameter is true
// because that means the user submitted a "Customize" change
if (session != null) {
Boolean hasChanged = (Boolean) (session.getAttribute( PortletRendererUtil.portletParameter(pr, "hasChanged")));
if (hasChanged != null) {
if (hasChanged.booleanValue()) { // cache has been invalidated
session.setAttribute( PortletRendererUtil.portletParameter( pr, "hasChanged"), new Boolean(false));
invalid = true;
}
}
}
// if the cache has not been invalidated, use the cached version
if (!invalid) {
String oldKey = HttpPortletRendererUtil.getCachedVersion(pr);
if (oldKey != null) {
String oldURL = oldKey.substring(oldKey.indexOf('.'));
// use cache only if URL hasn't changed
if (oldURL.equals(pr.getPageURL())) {
Integer cache_duration = data.getInteger("CACHE_DURATION");
long lastTime = Long.parseLong(oldKey);
// use cache if it hasn't yet expired
if ((currentTime - lastTime) < cache_duration.intValue() * 60000) {
HttpPortletRendererUtil.useCachedVersion(pr);
return;
}
}
}
}
HttpPortletRendererUtil.setCachedVersion( pr, String.valueOf(currentTime)+"."+pr.getPageURL(), HttpProvider.USER_LEVEL);
// Writes container -- handles rendering the border if necessary
PortletRendererUtil.renderPortletHeader(pr, out, data.getPortletTitle());
new ExchangeProvider().process(pr, "INBOX");
PortletRendererUtil.renderPortletFooter(pr,out);
}
catch (IllegalArgumentException ie)
{
...
}
}

 

Conclusion

Oracle9iAS Portal supports a runtime framework and three PDKs (PDK-Java, PDK-PL/SQL, and PDK-URL Services) that developers can use to build providers that interact with external applications to supply data to portlets.

OTN's Oracle9iAS Portal Community page provides additional resources for learning about and using Oracle9iAS Portal and the PDK. You can also visit Portal Studio, a hosted developer service that you can use to build and deploy Web portlets without having to install Oracle9iAS Portal yourself.


Questions or comments? Post a message in the Portal Development (PDK) discussion forum on OTN or send email to the author.

Building a Portlet for Microsoft Exchange: Integrating Applications with Oracle9iAS Portal
Author: Robert Hall, Oracle Corporation
Date: October 2001

This document is provided for information purposes only and the information herein is subject to change without notice. Please report any errors herein to Oracle Corporation. Oracle Corporation does not provide any warranties covering and specifically disclaims any liability in connection with this document.
Oracle is a registered trademark and Oracle9i is a trademark or registered trademark of Oracle Corporation. All other company and product names mentioned are used for identification purposes only and may be trademarks of their respective owners.

Oracle

Oracle Corporation
World Headquarters
500 Oracle Parkway
Redwood Shores, CA94065
U.S.A.

Worldwide Inquiries:
+1.650.506.7200
Copyright © Oracle Corporation 1999, 2000, 2001
All Rights Reserved


 

E-mail this page
Printer View Printer View
Oracle Is The Information Company About Oracle | Oracle RSS Feeds | Careers | Contact Us | Site Maps | Legal Notices | Terms of Use | Privacy