
|
Building a Portlet for Microsoft Exchange
Integrating Applications with Oracle9iAS
Portal
October 2001
|
 |
Contents

|
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
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.
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 PDK framework provides most of what a portlet provider needs
to access an external application like Microsoft Exchangethere'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 providerthe 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 datathe 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:
- 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 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
|