5.11 General portlet development guidelines

 < Day Day Up > 



5.11 General portlet development guidelines

The following general portlet performance thoughts have been taken directly from the Portlet Developer's Best Practice and Coding Guidelines. We consider performance crucial to any production portlet, therefore we have included a copy of this information here.

Portlet coding guidelines

These guidelines are intended to help you produce best-of-breed portlets for the WebSphere Portal environment.

Refrain from using instance variables. Portlets, like servlets, exist as a singleton instance within the server's JVM. Therefore, a single memory image of the portlet services all requests and must be thread-safe. Data stored in instance variables will be accessible across requests and across users and can collide with other requests. Data must be passed to internal methods as parameters. There are other means of storing data globally.

Pass data to the view (JSP) as a bean in the request object. Use the PortletRequest object to pass data to the view for rendering. This way, when the request is complete, the data falls out of scope and is cleaned up. Passing it as a bean allows the JSP to simply refer to the data as properties on the bean using intrinsic functions in the JSP syntax.

Use the portlet logging facility. Using the PortletTraceLogger installed with WebSphere Portal for tracing and logging allows your portlet's debug and trace output to be stored with other portlet output in the portal log files. It also takes care of the infrastructure around logging and tracing. The PortletLog object that is the interface to the logging mechanism can be obtained from the portlet context:

 PortletLog log = getPortletConfig().getContext().getLog(); 

You should also verify that logging is enabled for attempting to log data, especially if that requires additional logic or storage to build the log message.

Example 5-11: Using a log from portlet

start example
 if (log.isDebugEnabled()) { String logMsg = new String("Time to retrieve data: " + elapsedTime); log.debug(logMsg); } 
end example

Adopt good code documentation habits. While commenting of code is not required for the functionality of the portlet, it is essential for its maintenance. In addition to the fact that the responsibility for a portlet's maintenance can change hands over time, well-written portlets serve as models for other portlets, which means that someone else must understand what you wrote. Well documented code implies more than simply inserting comments, it implies good naming practices for Java resources as well. The following are examples of guidelines to follow:

  • Insert JavaDoc-compliant prologues for all public classes, variables, and methods. Be sure to document inputs and outputs.

  • Include inline comments, but do not include them on the same line as Java source. It is difficult to align and follow comments which are on the same line as Java source.

  • Use meaningful names for variables and methods. Variables x, y, z, while easy to type, are not easy to follow through code. Capitalization conventions and the use of underscores ('_') within resource names is a matter of personal choice, but be consistent once your choice is made.

Use the ContentAccessService to fetch external content, when necessary. If it is necessary to fetch external content using an HTTP request, use the ContentAccessService as it is intended to perform that function as expediently as possible. It also prevents you from having to rewrite the same function and introduce another maintenance point.

Cache portlet settings or portlet data. If portlet settings or data is complicated and requires complex parsing to digest, it may be a good idea to cache the data for quick retrieval later. Avoid using the PortletSession to store this data as it causes "session bloat". Alternatively, consider using the Application Server's Dynacache CommandCache to store this data. PortletSettings can be stored keyed off the portlet's name. PortletData will have to be stored keyed off the portlet name and user name. When updates to the cache are necessary, based on updates to PortletData or PortletSettings, devise a means of communicating updates to the data, such as through implementing the PortletSettingsAttributesListener interface, or by using a "dirty bit" that can be interrogated to see if updates have been made to the configuration, in which case the cache can be invalidated and updated.

Note that the CommandCache is not currently cluster-aware, meaning that cached content is not shared across horizontal nodes in a cluster. So, portlets must be able to create the cache entry from scratch if the cache entry does not exist.

Follow design guidelines for Struts portlets.

If you are developing portlets based on Struts, use the following additional guidelines to ensure your implementation is the best it can be:

  • Be sure to use the sample portlet applications that accompany the Struts Portal Framework package to ensure Struts is working properly in your environment before testing Struts support in your portlet.

  • Make sure to review the documentation on the Struts Portal Framework for application requirements and any restrictions. For existing Struts applications refer to the section in the same document titled "Migrating an Existing Struts Application".

Portlet packaging guidelines

Make common functions available externally to portlets. If the portlet contains common functions that are replicated across several portlets, consider isolating them making them externally accessible by the portlets. The easiest way to do this is build another JAR file of the common classes and place the JAR file in a location that is in each portlet's classpath, such as the AppServer/lib/app directory. Another approach is to build a Portlet Service from the common functions which can then be accessed using the common PortletService interface, retrieved using the PortletContext.getService() method.

Combine portlets with similar functions into a single portlet with multiple configuration settings. If you find yourself developing several similar portlets, such as a mortgage calculator portlet, car loan calculator portlet, and a home equity loan calculator, it might make sense to actually develop a single portlet that can behave differently based on configuration settings. The common portlet code is specified as part of the portlet application's abstract portlet definition. The application's various concrete portlet definitions have unique configuration parameters (PortletSettings) that can be used to customize the behavior of the common portlet code.

Data management

There are three primary areas where portlet configuration information is stored and can be maintained: portlet settings, portlet data, and servlet configuration.

  • Use portlet settings to store user-independent configuration data. This data represents configuration parameters from the portlet's portlet.xml file (<config-param> elements under the concrete portlet definitions). It is only writable in the portlet's configure mode (doConfigure() method), but is readable through the getPortletSettings() method on the portlet.

  • Use portlet data to store user-dependent configuration data. This data often represents personalized overrides to the portlet settings and is thus stored according to user and portlet instance. This data is only writable from a portlet's edit mode (doEdit() method), but is readable through the getPortletData() method on the PortletRequest object.

  • Use servlet configuration for storing static initialization information for a portlet. Examples of this information include the install directory or path information. This data, taken from the portlet's web.xml file, is read only and accessible using the getServletConfig() method on the portlet.

General session management guidelines

Limit the use of the portlet session for storing portlet state information. The Portlet Session is a convenient place to store global data that is user and portlet specific and that spans portlet requests. However, there is considerable overhead in managing the session, both in CPU cycles as well as heap consumption. Since sessions may not expire for quite some time, the data contained in the sessions will remain resident in active memory even after the user is done with the portlet. Be very judicious about what is stored in the Portlet Session.

Tip 

A good rule of thumb to use when determining if data should be stored in the Portlet Session is if the data is user-specific and cannot be recreated by any other means, such as portlet state information.

For example, parsed configuration data (from PortletSettings) should not be stored in the Portlet Session since it can be recreated from the database at any time.

Do not rely on portlet sessions if the portlet is to allow anonymous access. If your portlet is to be used by unauthenticated users, such as on the Welcome page, then the portlet should not rely on the portlet session at all. Portlet sessions are bound to a user context. By default, for unauthenticated users, a portlet session can be requested by a portlet, but it is a temporary session which only lasts for the duration of the current request. Public session support exists, where sessions are issued to unauthenticated users, but because there is no log out action for an unauthenticated user, the session will not be invalidated until it times out, which can lead to severe memory consumption issues.

There are other ways to preserve global data for anonymous access, such as:

  • Storing data in a collection whose key is communicated back to the client using a cookie.

  • Storing data in the WebSphere Application Server's dynacache facility, again using a unique key which is communicated to the browser using a cookie.

  • Storing the state information as a cookie itself.

  • Storing non-sensitive data as hidden field elements within FORMs so that it gets passed back on subsequent form submission. This has the added advantage of binding state-aware data to the page requiring it.

Alternatively, consider limiting the function of the portlet for anonymous access. You can check for the presence of a user object (PortletRequest.getUser()) to see if a user has logged into the portal, in which case a portlet session should be available.

Always request an existing portlet session. Always request a portlet session using either PortletRequest.getPortletSession() or PortletRequest.getPortletSession(false), which will only return a session if one does not already exist. The portal server will establish a portlet session for a portlet before it is invoked. This helps prevent the case where a temporary session is generated for a portlet during anonymous (unauthenticated) access.

Prevent temporary sessions from being generated in the JSP: Add the JSP page directive <%@ page session=" false" %> to the JSP to prevent temporary sessions from being created by the JSP compiler if none already exist. This will help guard against attempting to use the session to store global data if the session will not exist past the current request. You will need to be sure the portletSession exists before trying to use it.

Other general development guidelines

There are potentially many different types of browsers, or user agents, which access WebSphere Portal besides a typical desktop browser, such as hand-held devices (PDAs, or personal data assistants) and wireless phones. The capabilities of these devices vary widely, as do their users' attention spans and interaction models. Therefore, the portlet's design needs to take these facts into consideration.

Information priority is different depending on the device it is viewed on. Capabilities of desktop browsers are limitless. Users can download almost anything quickly and randomly navigate through it. For handheld devices, however, screen real estate and device memory is at a premium, not to mention the relatively slow speed of a wireless connection versus a LAN connection. Also, users of mobile and handheld devices need quick access to concise information and cannot afford the time or effort it takes to navigate through several screens to get to the desired information. Here are some more general guidelines for portlet development:

All portlet strings should be fetched from resource bundles. All displayable strings should be stored in resource bundles and fetched using the ResourceBundle Class. The resource bundles should be organized by language under the portlet's WEB-INF/classes directory.

For example, if a portlet supports English and Finnish Language, it would have three resource bundles (default, English, and Finnish) and might be organized under WEB-INF/classes as so:

 WEB-INF/classes/nls/mystrings.properties WEB-INF/classes/nls/mystrings_en.properties WEB-INF/classes/nls/mystrings_fi.properties 

Using the ResourceBundle class, you would refer to the properties file as "nls.mystrings". Java will automatically look for the appropriate properties file based on the current locale

Organize portlet help files by language. As opposed to typical JSPs in the portlet's view, the portlet's help JSP files can, and should be language dependent. Since little else in the JSP will exist but help content, there isn't much need in extracting strings from resource bundles. Translate the JSPs instead, and organize them by language. When using the include() method on the portlet context to dispatch a JSP, the method will search the directory structure for locale-specific subdirectories first. For example, if you include the JSP "/WEB-INF/helpjsps/html/myHelp.jsp", and the current request locale is en_US, then the method will look for the myView.jsp in the following directories, in order:

 /WEB-INF/helpjsps/html/en_US/myHelp.jsp /WEB-INF/helpjsps/html/en/myHelp.jsp /WEB-INF/helpjsps/html/myHelp.jsp /WEB-INF/helpjsps/myHelp.jsp 

Note that the include() method will also look for a default myHelp.jsp if one does not exist in the HTML subdirectory.

Use portlet messaging to communicate (send messages) to other portlets on the same page. It is possible to send messages to a specific portlet or to all portlets on a page. The message body may contain data that the receiving portlets require. The message is sent from a portlet's actionPerformed() method and received by other portlet's messageReceived() methods, before those portlet's doView() is called as a result of the action, so there is an opportunity to set data or state information before the receiving portlets are invoked. See the section titled Developing portlets Writing portlets Portlet messaging in the WebSphere Portal InfoCenter for more details.

Avoid the use the HttpSession to share data with other portlets/servlets. The HttpSession is not common between nodes in a cluster, so any data store in the HttpSession is only visible by J2EE resources in the same Application Server instance. The PortletSession is derived from the HttpSession, and besides only having visibility of the portlet instance, is also bound by the same restriction. Persistent sessions can be turned on in the application server, which will serialize the session to a database which can be shared across nodes in a cluster, but that can cause performance issues for very large sessions. See Session Management guidelines section for other ideas.

Avoid the use the HttpSession to share data with other portlets/servlets. The HttpSession is not common between nodes in a cluster, so any data store in the HttpSession is only visible by J2EE resources in the same Application Server instance. The PortletSession is derived from the HttpSession, and besides only having visibility of the portlet instance, is also bound by the same restriction. Persistent sessions can be turned on in the application server, which will serialize the session to a database which can be shared across nodes in a cluster, but that can cause performance issues for very large sessions. See Session Management section for other ideas.

Performance considerations guidelines

Do not spawn threads. Since portlets are multi-threaded to begin with, spawning child threads can create problems with synchronization or multi-threaded access to the spawned threads. Threads should be used with extreme caution, and when necessary, should be used briefly (no long running threads, especially any that outlast a request).

Do not use threads to access J2EE resources. In certain Java Virtual Machine (JVM) implementations, such as on zOS, there are a limited number of threads in the process thread pool which are allocated for accessing J2EE resources, such as JCA connectors. All such resources should be manipulated on the calling thread to minimize the possibility of starving the thread pool.

Limit temporary storage. Temporary storage includes temporary variables created within a block of code which are used then discarded. Temporary variables are typically utilitarian in nature, such as Strings, integers, booleans, Vectors, and such. However simple in nature, temporary variables take CPU cycles to create and destroy and occupy space on the heap which must be maintained by the garbage collector. The following is a list of simple guidelines for reducing or optimizing temporary storage:

  • Reuse temporary variables as much as possible

  • Declare temporary variables outside loops

  • Instead of String, use StringBuffers, which are optimized for parsing and string assembly

  • Declare collection classes (Vectors, Arrays) with an initial size that is the average maximum size, to prevent growth and reallocation of space on the heap.

Avoid synchronized methods. Synchronization causes a bottleneck through your portlet, or a path that only allows one thread at a time to pass through. Besides slowing the entire portal system, the possibility of a deadlock occurring is also high, which could hang the portlet, or portal, for all users.

Avoid long-running loops. Simply put, portlets need to be fast. Long running loops consume a large number of CPU cycles and cause the portal page to wait for this one portlet to finish. If a long-running loop seems necessary, re-inspect the design to see if it can be accomplished by some other means, such as through block/notification logic, or breaking the large loop up into several shorter ones.

Use JSPs instead of XML/XSLT. JSP are more efficient than XML/XSLT in general. Since JSPs are compiled into servlets, they run much faster than having to parse XML and apply XSL stylesheets on every request. Also, the portal infrastructure is optimized around JSPs, allowing for easy expansion into other markups, languages, and even browser support by simply adding new directory structures of JSPs. In general, XML parsing and XSLT processing is expensive. XSL processing, for example, causes hundreds of transient String objects to be created and destroyed, which is expensive in CPU cycles and heap management. If XML/XSLT is a requirement, then make use of caching as much as possible to reduce the amount of parsing and XSLT processing which must take place.

Use caching as much as possible. If portlet output is static in nature or is valid for some period of time before it is updated, the portlet should enable the "display cache" by setting appropriate values for the <cache> elements in the portlet's portlet.xml file (note that caching must be enabled in a portlet before it is installed or updated). A portlet's caching settings can be set such that the output is never cached (<expires> value of 0, the default), cached for some period of time (<expires> value greater than 0, in seconds), or cached indefinitely (<expires> value of -1). The cached entries can also be shared across all users of the same portlet (<shared> value of YES) if the output is valid for any user.

Important: 

Using the cache vastly improves the performance of the portlet, and thus of the page in general.

The portlet can also implement the getLastModified() method to control the invalidation of the cached content, informing the portal which it should call the portlet's doView() method to refresh the output. Refer to the portlet development section of the WebSphere Portal InfoCenter for examples of how to use the getLastModified() method.



 < Day Day Up > 



Portalizing Domino Applications for Websphere Portal
Portalizing Domino Applications for Websphere Portal
ISBN: 0738499811
EAN: 2147483647
Year: 2003
Pages: 103
Authors: IBM Redbooks

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net