TOC PREV NEXT INDEX

Developing Portlets with ICEfaces


ICEfaces provides the same benefits to portlets as it does to web applications. This section describes how to use ICEfaces for portlet development.

ICEfaces Portlet Configuration

The current specification for enterprise Java portlets is JSR 168, which details the portlet-specific APIs that are supported by the portal container as well as the configuration artifacts that are required. In addition, vendors implementing JSR 168 typically have their own custom configuration options. The instructions in this section outline both the general and vendor-specific requirements for developing and deploying ICEfaces-powered portlets.

JSF Portlet Bridge

Because ICEfaces is an extension to JavaServer Faces (JSF), the typical way to run standard JSF-powered portlets in a compliant portal is to use a bridge. There are currently several different bridges to choose from as well as a group working to standardize an interface for JSF portlet bridges to follow (JSR 301).

The problem with current bridge implementations is that they vary in how they adapt to the JSF implementation as well as the portal implementations they work with. This makes the portability of JSF portlets a challenge. Each bridge also hooks into the JSF implementation in ways that are currently incompatible with ICEfaces. If you are developing ICEfaces portlets, do not include a JSF portlet bridge library.

The portlet.xml

With portlets, the main configuration document is the portlet.xml file where one or more portlets are declared along with their specific configuration information. For a comprehensive discussion of the contents of the portlet.xml file, refer to the portlet specification (JSR 168). For developing ICEfaces applications, you only need to be concerned with a couple of important settings: <portlet-class> and <init-param>.

Note: In the following example, boldface text is used only to call attention to the relevant settings in the code example.

The following is a sample code snippet of a portlet declaration with the specific ICEfaces information added:

... 
 
<portlet> 
 
  <portlet-name>toggleMessage</portlet-name> 
 
  <display-name>Toggle Message</display-name> 
 
  <portlet-class> 
 
     com.icesoft.faces.webapp.http.portlet.MainPortlet 
 
  </portlet-class> 
 
  <init-param> 
 
    <name>com.icesoft.faces.portlet.viewPageURL</name> 
 
    <value>/toggle.iface</value> 
 
  </init-param> 
 
</portlet> 
 
... 
 

The <portlet class> is the fully qualified class name of the class that implements the Portlet interface. ICEfaces has its own portlet, which could be considered a bridge of sorts, that handles incoming calls from the portal container and passes them on to the ICEfaces framework. You must use this portlet or a subclass of this portlet as the <portlet-class> value. The <init-param> setting uses a key of com.icesoft.faces.portlet.viewPageURL. The value of this parameter is the initial view that the portlet displays on the initial render pass. It is important to use the .iface extension to ensure that the request is properly handled by the ICEfaces framework. The parameters for the other portlet modes are com.icesoft.faces.portlet.editPageURL and com.icesoft.faces.portlet.helpPageURL.

Note: The parameter names for the supported portlet modes have changed to be more descriptive and consistent with other ICEfaces parameters. The old ones, com.icesoft.faces.VIEW, com.icesoft.faces.EDIT, and com.icesoft.faces.HELP, are still supported, but have been deprecated. You should use the following new parameters for current and future portlet development:
com.icesoft.faces.portlet.viewPageURL com.icesoft.faces.portlet.editPageURL com.icesoft.faces.portlet.helpPageURL
The <ice:portlet> Component

ICEfaces provides a portlet component that you should use to wrap around the entire content of your portlet. It is implemented as a NamingContainer so that it can apply the portlet namespace as the top level of the JSF ID hierarchy. Doing this makes the ID hierarchy more efficient and helps the ICEfaces framework uniquely identify components on the page, which is important when more than one ICEfaces portlet is running.

To use the portlet component, add it as the top-level component of your content. For example, the following is an ICEfaces page that toggles a message on and off. You can see that the portlet component is wrapped around the actual content of what we want to see in the portlet.

<f:view xmlns:h="http://java.sun.com/jsf/html"
 
        xmlns:f="http://java.sun.com/jsf/core"
 
        xmlns:ice="http://www.icesoft.com/icefaces/component">
 
  
 
  <ice:portlet>
 
    <ice:form>
 
      <ice:panelGrid columns="2">
 
        <ice:outputText value="Message:" />
 
        <ice:outputText value="#{test.message}" />
 
        <ice:commandButton value="Toggle" actionListener="#{test.toggle}"/>
 
      </ice:panelGrid>
 
    </ice:form>
 
  </ice:portlet>
 
  
 
</f:view>
 

The ICEfaces portlet component is designed to behave unobtrusively in a regular web application. It is fairly common practice for developers to run their portlets as a web application to speed development and to check for portlet specific issues. In a portal container, the <ice:portlet> is rendered as a <div> element with an ID attribute set to the portlet instance's unique namespace. In a web application, the portlet namespace is not available so the component simply renders out as a <div> with an ID attribute that is automatically generated by the JSF framework. Because it is a NamingContainer, the ID of the <ice:portlet> component will be prefixed to the client ID of the nested sub-components.

Setting com.icesoft.faces.concurrentDOMViews

This guide documents a context parameter called, com.icesoft.faces.concurrentDOMViews. It is set in the web.xml file as follows:

<context-param>
 
  <param-name>com.icesoft.faces.concurrentDOMViews</param-name>
 
  <param-value>true</param-value>
 
</context-param>
 

 

In a normal web application, setting this to true indicates that ICEfaces should support multiple views for a single web application and tells the ICEfaces framework to treat each view separately. Typically this is enabled when you want to use multiple windows of a single browser instance to concurrently view an ICEfaces application. In a portlet environment, the framework needs to treat the separate portlets on a single portal page as distinct views so it is almost always necessary (and therefore safest) to have this parameter set to true.

Using the Portlet API

Configuring your portlet is half the battle but, as a developer, you'll likely want to access the Portlet API to do a few things.

ExternalContext

The JSF API was designed to support access to both the Servlet API (for web applications) as well as the Portlet API by exposing an abstract class called the ExternalContext. In your code, you get access to the ExternalContext as follows:

FacesContext facesContext = FacesContext.getCurrentInstance();
 
ExternalContext externalContext = facesContext.getExternalContext();
 
Attributes

Once you have a reference to the ExternalContext, you can access information from the Portlet API. The ExternalContext API provides methods to get information in a way that is independent of the environment that it is running in. For example, to access request attributes, you can do the following:

Map requestMap = externalContext.getRequestMap(); String uri = 
(String)requestMap.get("javax.servlet.include.request_uri"); 
 

 

The requestMap contains all the attributes associated with the request. Check the ExternalContext JavaDoc to see what is provided by the rest of the API as some methods state specifically what they do differently in a portlet environment as compared to a servlet environment.

PortletConfig

If you need to access the PortletConfig, you can use the requestMap. From there, you can retrieve information specific to the current portlet's configuration:

PortletConfig portletConfig = (PortletConfig)requestMap.get("javax.portlet.config");
 
String portletName = portletConfig.getPortletName();
 
String view = portletConfig.getInitParameter("com.icesoft.faces.VIEW");
 
PortletRequest, PortletResponse

You can directly access copies of the PortletRequest and PortletResponse objects using the ExternalContext:

PortletRequest portletReq = (PortletRequest)externalContext.getRequest();
 
PortletResponse portletRes = (PortletResponse)externalContext.getResponse();  
 

 
PortletSession

You can use the ExternalContext object to access the portlet session for storing and retrieving session attributes.

PortletSession 

		portletSession = (PortletSession)externalContext().getSession(false);
 

 

Attributes in the PortletSession object can be stored in different scopes. The default is PORTLET_SCOPE, which means the attributes are only visible to the individual portlet. The other option is APPLICATION_SCOPE where the attributes are visible to all the portlets. By default in a portal environment, when you define a session-scoped managed bean in JSF, the scope is PORTLET_SCOPE. For example, the following calls are equivalent:

Object portletAttribute = portletSession.getAttribute(key);  
 
Object portletAttribute = portletSession.getAttribute
 
															(key, PortletSession.PORTLET_SCOPE); 
 

If you want to store and retrieve attributes that are visible to multiple portlets, which can be useful when doing Ajax Push, you should ensure that you use the application scope parameter:

Object applicationAttribute = portletSession.getAttribute
 
																	(key, PortletSession.APPLICATION_SCOPE); 
 
PortletPreferences

Once you have the PortletRequest, you can access the PortletPreferences which can be used to get and set preferences:

PortletPreferences prefs = portletReq.getPreferences();
 
try {
 
    prefs.setValue("prefKey", "prefVal");
 
    prefs.store();
 
} catch (ReadOnlyException e) {
 
    e.printStackTrace();
 
} catch (IOException e) {
 
    e.printStackTrace();
 
} catch (ValidatorException e) {
 
    e.printStackTrace();
 
Portlet Styles

The JSR 168 specification also documents a base set of common styles that should be applied to specific page elements. By documenting these style names, portlets can be developed that adhere to a portal container's overall "theme-ing" strategy. For example, the style, portlet-form-button, is a style name that is used to determine the style of the text on a button. When running in a portlet environment, ICEfaces extended components render the portlet specific style classes by default. If you don't want these styles rendered out for your portlet, you can specify the following context parameter in the web.xml file and set it to false.

<context-param>
 
    <param-name>com.icesoft.faces.portlet.renderStyles</param-name>
 
    <param-value>false</param-value>
 
</context-param> 
 

 

However, because the portlet specification does not cover every rich component that is offered by ICEfaces (e.g., calendar), it is possible that some components may not match the current theme of the portal page.

Additionally, ICEfaces provides themes of its own (see Styling the ICEfaces Component Suite). These stylesheets were designed with web applications in mind. For portlet developers, ICEfaces also provides a portlet-friendly version of the xp.css stylesheet. The xp-portlet.css stylesheet can be easily added to your portlet by including the following component.

<ice:outputStyle href="./xmlhttp/css/xp/xp-portlet.css" />
 
Supported Portal Implementations

ICEfaces currently supports the following portal implementations:

The process of developing and deploying portlets to the various portal containers is vendor-specific so you should be familiar with the platform you are using. If you have questions, consult the portal vendor's documentation for additional help. Issues that are specific to ICEfaces running on a specific portal implementation are covered in the following sections.

Liferay Portal

Liferay uses JavaScript to enhance both the developer and user experience. Portlet users can drag and drop portlets on the page without requiring a full page refresh. For the developer, Liferay makes it easy to hot deploy and load portlets dynamically, which can be a big advantage in speeding up the development cycle. However, deploying ICEfaces portlets in this manner can be problematic because the JavaScript that ICEfaces relies on may not get executed properly. Because the portal container is in control of the page, the ICEfaces JavaScript bridge (the client portion of the ICEfaces framework) is not the only JavaScript code on the page. Once the portal page has been loaded, the bridge's window.onload() logic won't get executed unless there is a full page refresh.

Fortunately, Liferay provides configuration parameters that allow the developer to specify that a full render pass is required. Doing this ensures that the ICEfaces bridge is properly initiated. The required parameters, render-weight and ajaxable, are specified in the liferay-portlet.xml configuration file. They are added to the portlet section as shown in this snippet:

<portlet>
 
  <portlet-name>clock</portlet-name>
 
  <instanceable>true</instanceable>
 
  <render-weight>1</render-weight>
 
  <ajaxable>false</ajaxable>
 
</portlet>
 
Note: In the above example, boldface text is used only to call attention to the relevant parameters in the code example.

By setting these parameters, you ensure that a full-page refresh is done when the portlet is added to the portal page which, in turn, ensures that the ICEfaces JavaScript bridge is initialized correctly.

JBoss Portal

No specific parameters are required to run ICEfaces portlets in JBoss Portal.

WebLogic Portal

No specific parameters are required to run ICEfaces portlets in WebLogic Portal.

Apache Jetspeed-2

There is a browser-specific issue when running ICEfaces portals on Jetspeed-2 which is related to parsing cookie paths. The problem prevents Firefox from working properly. To solve it, you should modify [jetspeed-root]/conf/server.xml and add the emptySessionPath="true" attribute to the <Connector> element. It should look something like this:

  <Connector port="8080" maxHttpHeaderSize="8192" maxThreads="150" 
 
             minSpareThreads="25" maxSpareThreads="75" enableLookups="false" 
 
             redirectPort="8443" acceptCount="100" connectionTimeout="20000" 
 
             disableUploadTimeout="true" emptySessionPath="true"/>
 
               
 

This attribute is set by default in other Tomcat-based portals like JBoss Portal and Apache Pluto.

Apache Pluto

No specific parameters are required to run ICEfaces portlets in Apache Pluto.

Using Ajax Push in Portlets

By default, Ajax Push is active in ICEfaces. The configuration parameter that controls this is com.icesoft.faces.synchronousUpdate. If you don't need server-initiated rendering, then you should set this parameter to true by adding the following to your web.xml file:


 
<context-param> 
 
  <param-name>com.icesoft.faces.synchronousUpdate</param-name> 
 
  <param-value>true</param-value> 
 
</context-param> 
 

 

If you are using Ajax Push then you can set the parameter to false, or just leave it out altogether.

Development and Deployment Considerations

To maximize the successful development and deployment of ICEfaces portlets, consider these tips and suggestions:

Mixing Portlet Technologies

The sheer size and complexity of the matrix of portlet technology combinations makes it difficult to test and/or document every combination. While it is possible to deploy ICEfaces portlets on the same portal page as portlets built without ICEfaces, you should consider the following sections.

Static Portlets

We define static portlets as portlets that are built using more traditional technologies and without Ajax. These portlets follow the usual portlet lifecycle in that, when an action is taken on one portlet, a render is performed on all of the portlets on the page and a full-page refresh is triggered. If you mix ICEfaces portlets on a portal page with these traditional portlets, you'll get the behavior of the lowest common denominator. What this means is that if you interact with a static portlet, the regular lifecycle is engaged and all the portlets will re-render which triggers a page refresh. This behavior can undermine the benefits of using ICEfaces in your portlet development. If possible, you should consider porting the static portlets to use ICEfaces.

Dynamic Portlets

Several web development products, libraries, and component suites allow you to build rich, interactive portlets. Other products that use Ajax techniques (client-side, server-side, JSF, etc.) will have their own JavaScript libraries. JSF-based solutions can hook into the implementation in incompatible ways.

While running portlets built with these other technologies may work, there is a definite possibility of conflict with ICEfaces. While ICEfaces strives to co-exist with these other offerings, you can increase your chances of successfully building your project by reducing the complexity of your architecture. This can mean going with a single technology throughout or perhaps constraining a single portal page to a single technology.

ICEfaces With and Without Ajax Push

It is possible to combine synchronous and asynchronous ICEfaces portlets on a single portal page without any kind of additional configuration, as long as the asynchronous ICEfaces portlets are deployed from the same .war file. However, if the asynchronous ICEfaces portlets running on a single page are deployed in different .war files, you will need to configure the Push Server on the portal as well. Refer to Push Server, for more information. The required configuration depends on the portal implementation being used.

Note: All ICEfaces portlets on the same portal page are required to use the same version of ICEfaces.
Portlet Deployment - Same or Separate Archive

It is possible to deploy individual ICEfaces portlets in separate web archives (.war files) as well as bundling several ICEfaces portlets into a single archive. There are considerations for each. If multiple ICEfaces portlets are bundled together in a single archive, they gain the ability to share some common state and, using Ajax Push, benefit from a form of inter-portlet communication. However, these portlets also share a single set of configuration files (web.xml, portlet.xml, etc.) so it's important to understand the implications.

Shared faces-config.xml

The faces-config.xml file is the JSF configuration file used to describe managed beans, navigation, and other JSF-related features. Deploying multiple JSF portlets in a single archive means that all the portlets will share the same configuration. However, each portlet will, in essence get a copy of that configuration. This is an important distinction for the scope of managed beans and bean inter-dependencies.

Application-scoped beans are visible across all the portlets in the archive (as you would expect) and this can be useful if all the portlets need to share some common state. Application-scoped beans, together with Ajax Push, can be used to do a form of inter-portlet communication.

Session-scoped beans are scoped to the portlet session (rather than the user session) which means that each portlet gets its own instance. You can store information in the global user session so that it is available to all the portlets in a single user's session by using the Portal API and specifying the application scope.

Request-scoped beans are scoped to the requests for the individual portlet. Because ICEfaces directly handles all its own Ajax traffic bypassing the portal container, it can be easy to make incorrect assumptions about the number of beans being created and the relationship between them.

For example, consider the following set of managed JSF beans:

<managed-bean>
 
    <managed-bean-name>app</managed-bean-name>
 
    <managed-bean-class>com.icesoft.example.AppBean</managed-bean-class>
 
    <managed-bean-scope>application</managed-bean-scope>
 
</managed-bean>
 

 
<managed-bean>
 
    <managed-bean-name>sess</managed-bean-name>
 
    <managed-bean-class>com.icesoft.example.SessionBean</managed-bean-class>
 
    <managed-bean-scope>session</managed-bean-scope>
 
    <managed-property>
 
        <property-name>appBean</property-name>
 
        <value>#{app}</value>
 
    </managed-property>
 
</managed-bean>
 

 
<managed-bean>
 
    <managed-bean-name>req</managed-bean-name>
 
    <managed-bean-class>com.icesoft.example.RequestBean</managed-bean-class>
 
    <managed-bean-scope>request</managed-bean-scope>
 
    <managed-property>
 
        <property-name>sessionBean</property-name>
 
        <value>#{sess}</value>
 
    </managed-property>
 
</managed-bean>
 

 

Now let's assume we have two portlets, A and B, declared in the .war file and actively deployed on the page. The application-scoped bean is only created once and that single instance is referenced by both portlets. A single, separate session bean is created for each portlet. A separate request bean instance for each portlet is created for each request. Confusion occurs if the developer assumes that the session and request beans for portlet A can reference or see the session and request beans for portlet B. Even though we have a single faces-config.xml file, each portlet essentially uses its own copy of the configuration and other than the application-scoped beans, does not share any reference to each other's beans.

If your portlets don't need to share state for any meaningful reason, then it is probably better to deploy them in separate archives. If, for some other reason, they are deployed in the same archive, take care to manage the JSF configuration.

Inter-Portlet Communication (IPC)

The use of Ajax Push allows portlets to be updated based on server-side events that change the state of the current view. This can be a powerful feature that can also be leveraged to do a form of inter-portlet communication (IPC) in certain configurations.

IPC is only mentioned in the Portlet 1.0 spec (JSR 168) but is formally defined in the Portlet 2.0 specification (JSR 286). It is architected as an Event/Listener model. However, it is possible to use the ICEfaces Ajax Push mechanism to update portlets based on changes to the underlying model.


The way to do this currently with ICEfaces is to:

For an example of how to do this, review the sample ICEfaces Chat portlet.

ActionRequest and ActionResponse

The standard way for a portlet to change the state of its underlying model is via an ActionRequest. The portlet developer generates interactive controls like buttons and links using the Portlet API. When the user of the portlet interacts with those controls, the portal container can interpret the incoming request as an ActionRequest and process it accordingly.

Currently, all interactive types of actions are handled by the ICEfaces framework directly, and are therefore never handled by the portal container. In other words, ActionRequests are never really issued. While this is transparent to the developer, it does lead to certain restrictions. The API for the ActionResponse includes methods for setting the mode of the portlet as well as the state of the portlet window programmatically. Because ICEfaces bypasses the portal container for these types of requests, the ActionRequest and ActionResponse instances are not available to the portlet developer.

A future release of ICEfaces may deal with this situation but, for now, this is how it impacts the portlet developer.

Portlet Modes and Navigation

The portlet specification defines some standard modes that a portlet can support (VIEW, EDIT, HELP) and allows portal developers and/or portlet developers to potentially add their own. The impact of not being able to change portlet modes programmatically is that it becomes possible to have JSF navigation rules change the current view to a known mode without the portal container knowing about it. For example, you could allow a user to click a button that takes them from the EDIT mode back to the VIEW mode via JSF navigation rules. But because ICEfaces does this without going through the portal container, the portal container does not know the mode has changed and since the portlet developer cannot access the ActionResponse API, there is no way to let it know.

Some suggestions for dealing with this are:

Window States

Portlet windows can also be in various states (e.g., MINIMIZED). As with portlet modes, we recommend using the title bar icons to control these states rather than trying to adjust them programmatically.

Request Attributes

A unique aspect of the ICEfaces framework is that, due to the use of Ajax techniques, requests can be "long-lived"- somewhere between request and session scope. To maintain the long-lived nature of these types of requests, the ICEfaces framework needs to maintain request-based information during Ajax communications. To maintain this data, ICEfaces makes use of the standard servlet and portlet APIs to get and store things like attributes in its own internal structures. The PortletRequest API provides a couple of different ways to retrieve request attributes:

java.lang.Object getAttribute(java.lang.String name)
 
java.util.Enumeration getAttributeNames()
 

Unfortunately, there is an issue with certain portal containers where the getAttributeNames() method does not return the same set of attributes that can be retrieved with calls to getAttributeName(String name). In order for ICEfaces to ensure that all the required attributes are maintained for all Ajax requests, some request attributes need to be formally specified by the developer so that ICEfaces can copy them.

The ICEfaces framework maintains all the attributes that are specified in the JSR 168 specification (javax.servlet.include.*, javax.portlet.*). For attributes that are specific to the portal container, ICEfaces provides a mechanism for the developer to add them. Custom request attributes can be specified as space separated strings in com.icesoft.faces.portlet.hiddenAttributes context parameter. For example, to add Liferay's custom THEME_DISPLAY attribute so that it is properly maintained during Ajax requests, you would have the following context parameter set in your web.xml file:

<context-param> 
 
  <param-name>com.icesoft.faces.portlet.hiddenAttributes</param-name> 
 
  <param-value>THEME_DISPLAY</param-value> 
 
</context-param> 
 

 
Note: The parameter name for the custom attribute has changed to be more descriptive and consistent with other ICEfaces parameters. The old parameter, com.icesoft.faces.hiddenPortletAttributes, is still supported but has been deprecated. You should use the new parameter, com.icesoft.faces.portlet.hiddenAttributes, for current and future portlet development.

As noted, additional attributes can be added to the context parameter as long as they are separated by spaces.

Custom Solutions

If the portal vendor provides a client-side or server-side mechanism for handling these scenarios, then you have the option of using those. However, committing to these APIs probably means giving up the ability to run the portlet as a plain web application or running the portlet on a different portal platform. You can decide whether the trade-off is acceptable.

Running the ICEfaces Sample Portlets

In the binary installation of ICEfaces, pre-built sample applications and their sources can be found at:


[install_dir]/icefaces/samples/

Samples that have been specifically developed as portlets can be found at:


[install_dir]/icefaces/samples/portlet/

If you downloaded the source code distribution of ICEfaces, then you can build all the samples by running:

ant

from the following directory:

[install_dir]/icefaces/

The default build of the portlet samples is Liferay running on Tomcat 6.0. However, additional portlet targets are available. Use "ant help" to see the list of available portlet container targets.

ant -p

Deploying portlet .war files to a supported portal container varies from vendor to vendor. Refer to the documentation for your platform on how to deploy the portlet archive.

Component Showcase

The Component Showcase application is a useful way to see ICEfaces components in action as well as providing useful coding examples. We also provide a build of Component Showcase that can be deployed in a portal container. Each individual component demo has been configured as a separate portlet that can be added and removed from a portal page. The demo can be used to illustrate how to configure ICEfaces portlets as well as demonstrate working examples of the ICEfaces components running inside a portal.

Chat

The ICEfaces Chat Portlet example is a very simple application that shows how to use the Ajax Push features of ICEfaces to update multiple portlets on a page. In this case, you can open two separate instances of the chat portlet on a single page and chat between them, watching them both update as messages are sent.



Copyright 2005-2009. ICEsoft Technologies, Inc.
TOC PREV NEXT INDEX