Showing posts with label JSF. Show all posts
Showing posts with label JSF. Show all posts

Thursday, May 10, 2012

Building AJAX-enabled JSF Portlets in IBM WebSphere Portal 7

This note summarise different approaches to implement an AJAX-enabled JSF portlet in IBM WebSphere Portal 7. The portlet used in the demo has a simple UI that consists of two elements, one list box that has two items and one text label. When AJAX is enabled to the portlet, once the list box selected item changes, an AJAX request is sent to refresh the text label.

Implementation 1 - IBM JSF Portlet bridge 1.2 with IBM JSF Extension Components

IBM WebSphere Portal 7 comes with a JSF Portlet bridge 1.2 that supports JSF 1.2 based portlet development. As JSF 1.2 doesn't have built-in AJAX support, IBM JSF Extension Components are used to provide AJAX functionalities, as shown in the code below

<%@taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet"%>
<%@taglib uri="http://www.ibm.com/jsf/html_extended" prefix="hx"%>

<%@taglib
  uri="http://www.ibm.com/xmlns/prod/websphere/portal/v6.1/portlet-client-model"
  prefix="portlet-client-model"%>
<%@page language="java" contentType="text/html"
  pageEncoding="ISO-8859-1" session="false"%><portlet:defineObjects />
<portlet-client-model:init>
  <portlet-client-model:require module="ibm.portal.xml.*" />
  <portlet-client-model:require module="ibm.portal.portlet.*" />
</portlet-client-model:init>
<f:view>
  <hx:scriptCollector id="scriptCollector1">
    <h:form styleClass="form" id="form1">
      <h:selectOneListbox id="listbox1">
        <f:selectItem itemLabel="Label1" 
          itemValue="Value1" id="selectItem1" />
        <f:selectItem itemLabel="Label2" 
          itemValue="Value2" id="selectItem2" />
      </h:selectOneListbox>
      <hx:behavior event="onchange" target="listbox1" 
        behaviorAction="get" targetAction="group1"></hx:behavior>
    </h:form>
    <h:panelGroup styleClass="panelGroup" id="group1">
      <h:outputText styleClass="outputText" id="text1"
      value="Hello, #{param.listbox1} "></h:outputText>
    </h:panelGroup>
    <hx:ajaxRefreshRequest target="group1" 
      id="ajaxRefreshRequest1"
      params="listbox1">
    </hx:ajaxRefreshRequest>
  </hx:scriptCollector>
</f:view>

Implementation 2 - JSF 2.0 Portlet Bridge for IBM WebSphere Portal

There is a JSF 2.0 Portlet Bridge for IBM WebSphere Portal available from Greenhouse Lotus that allows developing portlets based on JSF 2.0 specifications with MyFaces 2.0. With JSF 2.0 build-in AJAX support, the demo can be implemented as JSF code shown in below.

<div xmlns="http://www.w3.org/1999/xhtml" 
 xmlns:ui="http://java.sun.com/jsf/facelets" 
 xmlns:f="http://java.sun.com/jsf/core"
 xmlns:h="http://java.sun.com/jsf/html" 
 xmlns:portlet="http://java.sun.com/portlet_2_0"  >
<f:view>
  <h:form> 
    <h:selectOneListbox styleClass="selectOneListbox" 
      id="listbox1" value="#{testBean.text}">
      <f:selectItem itemLabel="Label1" itemValue="Value1"/>
      <f:selectItem itemLabel="Label2" itemValue="Value2"/>
      <f:ajax render="text1" execute="@this"/> 
    </h:selectOneListbox>
    <h:outputText id="text1"
      value="Hello, #{testBean.text} ">
    </h:outputText>
  </h:form>
</f:view>
</div>

Implementation 3 - ICEfaces Enterprise Edition (EE) 3.0

ICEfaces Enterprise Edition 3.0 provides a PortletFaces Bridge Extensions library that supports JSF 2.0 based portlets on WebSphere Portal 7. The demo can be implemented using the same JSF code as Implementation 2 above with MyFaces 2.0 or Mojarra 2.1, additional change required is add the container runtime option below in portlet.xml.

<container-runtime-option>
  <name>javax.portlet.renderHeaders</name>
  <value>true</value> 
</container-runtime-option>

All three implementations are tested in WebSphere Portal 7.0.0.2 and WAS 7.0.0.19 with different theme and render modes, the results are summarised in the table below.

Implementation Page Builder theme - SSA Page Builder theme - CSA Portal theme - SSA only Notes
IBM JSF Portlet bridge 1.2 with IBM JSF Extension Components works as expected The portlet is rendered but AJAX doesn't work, no Javascript errors works as expected  
JSF 2.0 Portlet Bridge for IBM WebSphere Portal (MyFaces 2.0) works as expected works as expected works as expected The library is provided as is, with no support from IBM
ICEfaces Enterprise Edition 3.0 (MyFaces 2.0/Mojarra 2.1) works as expected The portlet is unable to render with exceptions from the bridge The portlet is rendered but AJAX doesn't work with Javascript errors Commercial license required

Monday, March 12, 2012

JSF Portlet File Download using WebSphere JSF 1.2 Portlet Bridge

This note summarise my experience of JSF portlet file download using WebSphere JSF 1.2 Portlet Bridge.

One approach to implement file download from portlets is to use the JSR 286 resource serving mechanism. In order to make it working in JSF portlets, one needs to mix portlet and JSF programming as there is no direct support of the mechanism from JSF 1.2 Portlet Bridge.

The implementation consists of three components: a portlet class that extends FacesPortlet and overrides the serveResource method; a JSF page that provides use the download link; and a JSF managed bean that provides the file content.

The portlet class

package demo;

import java.io.IOException;
import java.io.OutputStream;

import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
import javax.faces.context.FacesContext;
import javax.faces.lifecycle.Lifecycle;
import javax.portlet.PortletException;
import javax.portlet.PortletSession;
import javax.portlet.ResourceRequest;
import javax.portlet.ResourceResponse;

import com.ibm.faces.portlet.FacesPortlet;
import com.ibm.faces.portlet.httpbridge.PortletRequestWrapper;
import com.ibm.faces.portlet.httpbridge.PortletResponseWrapper;
import com.ibm.faces.portlet.httpbridge.ResourceRequestWrapper;
import com.ibm.faces.portlet.httpbridge.ResourceResponseWrapper;

public class JSFPortlet extends FacesPortlet {

 @Override
 public void serveResource(ResourceRequest request, ResourceResponse response)
   throws PortletException, IOException {

  super.serveResource(request, response);

  TestBean testBean = (TestBean) getJSFManagedBean(
    request, response, TestBean.BEAN_NAME, TestBean.class);

  final OutputStream out = response.getPortletOutputStream();
  byte[] bytes = testBean.getCsv();
  response.setContentType("text/csv");
  response.setProperty("Content-Disposition",
    "attachment; filename=download.csv");
  response.setProperty("Content-length", String.valueOf(bytes.length));

  out.write(bytes);
  out.flush();
  out.close();
 }

 public Object getJSFManagedBean(ResourceRequest request,
   ResourceResponse response, String beanName, Class beanClass)
   throws PortletException {

  PortletSession portletSession = request.getPortletSession();

  Object jsfBean = (TestBean) portletSession.getAttribute(beanName);

  if (jsfBean == null) {
   PortletRequestWrapper requestWrapper = new ResourceRequestWrapper(
     request);
   requestWrapper.setPortletContext(getPortletContext());
   PortletResponseWrapper responseWrapper = new ResourceResponseWrapper(
     response);
   Lifecycle lifecycle = getLifecycle(requestWrapper);
   FacesContext context = getFacesContext(requestWrapper,
     responseWrapper, lifecycle);

   final ExpressionFactory expressionFactory = context
     .getApplication().getExpressionFactory();

   final ValueExpression valueExpression = expressionFactory
     .createValueExpression(context.getELContext(), 
       "#{" + beanName + "}", beanClass);
   jsfBean = (TestBean) valueExpression.getValue(context
     .getELContext());
  }

  return jsfBean;
 }
}

The JSF page

<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<%@ taglib uri="http://java.sun.com/portlet_2_0" prefix="portlet"%>               
<%@taglib uri="http://www.ibm.com/xmlns/prod/websphere/portal/v6.1/portlet-client-model"   
       prefix="portlet-client-model" %>              
<%@ page language="java" contentType="text/html" pageEncoding="ISO-8859-1" session="false"%>
<portlet:defineObjects />
<portlet-client-model:init>
      <portlet-client-model:require module="ibm.portal.xml.*"/>
      <portlet-client-model:require module="ibm.portal.portlet.*"/>   
</portlet-client-model:init>  
 
       
<f:view>
  
  <portlet:resourceURL var="resourceUrl" >
  <portlet:param name="action" value="csv" />
  </portlet:resourceURL>

  <a href="<%=resourceUrl%>">Download CSV</a>
  
</f:view>

The JSF managed bean

package demo;
import java.util.Date;

public class TestBean {
 public static final String BEAN_NAME = "testBean";
 
 public byte[] getCsv() {
     return new Date().toString().getBytes();
   }
 
 public String getText() {
  return "Hello";
 }
}

Some notes are:

- The IBM JSF 1.2 Portlet Bridge doesn't provide a Facelet taglib xml for the <portlet:resourceURL> tag, so the tag cannot be used in Facelet based JSF pages. This issue has been addressed in IBM JSF 2 Portlet Bridge.

- The <portlet:resourceURL> tag provides a var attribute that can be used to export the resource URL. Based on the JSR 286 spec, the exported variable is created in JSP page scope. As JSF code has no access to JSP page scope, the exported variable can not be used in JSF code.

- In traditional Servlet programming, the response.setHeader method is used to set HTTP response heads like Content-Disposition and Content-length, but in portlet, the ResourceResponse.setProperty method is used.

- There are two ways to access JSF managed beans from the portlet code: if the managed bean is a session scope bean and it's already exists in the session, the portletSession.getAttribute(BEAN_NAME) call can be used; otherwise, JSF value expression can be used to get and/or create the bean from FacesContext, as the IBM JSF 1.2 Portlet Bridge doesn't provide any method that the portlet implementation class can directly access FacesContext, some internal code must be used to achieve this.

Thursday, March 8, 2012

File down with JSF applications

This note summarises different ways to implement file download with JSF applications.

Approach 1- File down using Servlet

This approach uses a servlet to serve file download request and the link to the servlet can be implemented by using various JSF components, e.g. commandLink and outputLink.

The servlet based approach is a simply and easy way to implement file down, however, the shortcoming here is the file serving process is outside the JSF life cycle, glue code is required when the download process needs to access JSF faces context and backing beans.

A detailed discussion of the approach can be found from here.

Approach 2- File down using JSF PhaseListener

This approach use a JSF PhaseListener to serving process, an example of such implementation can be found from the Mojarra Scales project, which provides a UI component that renders the download link and a PhaseListener that serves file content, sample usage is shown in below.

<sc:download id="downloadPdf" 
 method="download" 
 mimeType="application/pdf" 
 fileName="sample.pdf" 
 data="#{testBean.pdf}" 
 text="Download here">
</sc:download>

When the Mojarra Scales download component is used to create a download link, it renders a http link on the page with a specific URL format which the PhaseListener is used to identify the file request, once the link is clicked, the PhaseListener serves content in the beforePhase method at the RESTORE_VIEW phase.

By using a JSF PhaseListener, the file serving process is now part of the JSF lifecycle. However, the shortcoming here is the data for download is populated during page render, i.e. the evaluation of the #{testBean.pdf} expression in the above example, even the user has NOT click the download click.

Approach 3- File down using JSF Action/ActionListener

This approach use JSF Action/ActionListener that attaches to JSF commandLink or commandButton to serves file content.

Details discussion on Action based implementation can be found here, ActionListener based implementation can be found from the PrimeFaces FileDownload component.

This is the best approach from my point of view.