I have the privilege of speaking about Social Networking Portlets with ICEfaces Ajax Push at the following two conferences:
Liferay East Coast User Conference
May 21, 2009 Rivet Logic Corporation (Training Center) 1775 Wiehle Avenue, 3rd floor Reston, VA 20190 Map
CommunityOne West Here I'll be co-speaking with Ed Burns, who is co-chairman of the JSR 314 (JSF 2.0) Expert Group
June 1-3, 2009 The Moscone Center San Francisco Map
This is an open developer conference. Monday 6/1 is free!
The talks will focus on the power and elegance of Liferay + ICEfaces Ajax Push, and how these technologies can be used together to create Social Networking portlets with Liferay Portal.
Social networking is a natural addition to portals, already a meeting place for applications. Diverse systems and users can be brought together for web-based communication and collaboration. When introduced to Ajax Push, Portlets can provide real-timecommunication features, such as presence, chat, and new forms of application-specific interaction.
Here's a screen shot of two portlets I'll be demontrating: ICEfaces Friends, and ICEfaces Chat:
The friends portlet is presence-aware, in that it has an application-scoped bean that listens to users logging-in/out of the portal. When it receives one of those events, ICEfaces Ajax Push will fire a visual effect, alerting the user of the friend's online status. If online, then the chat icon is activated. If the user clicks on the chat icon, then ICEfaces Ajax Push is used to achieve inter-portlet communication with the Chat portlet on the right. A new chat session is created, and users can chat with one another in near-real-time, again thanks to ICEfaces Ajax Push.
One of the things that I love about JSF, is the elegant, clean separation of the Model, View, and Controller concerns of the MVC design pattern. Case in point: Here's the entire XHTML markup for the Chat portlet:.
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <f:view xmlns:c="http://java.sun.com/jstl/core" xmlns:f="http://java.sun.com/jsf/core" xmlns:ice="http://www.icesoft.com/icefaces/component" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:sn="http://www.liferay.com/jsf/facelets/socialnetworking"> <ice:portlet styleClass="portlet-content"> <sn:styling /> <ice:form> <ice:messages /> <c:set value="#{chatRoomsModel.roomsWithUser}" var="chatRoomsWithUser" /> <ice:panelTabSet rendered="#{!empty chatRoomsWithUser}" value="#{chatRoomsWithUser}" var="chatRoom"> <ice:panelTab label="#{chatRoom.name}"> <ice:panelGroup id="scroller" style="overflow: auto; width: 100%; height: 250px;"> <ice:dataTable rowClasses="results-row,results-row alt" width="100%" value="#{chatRoom.chatMessages}" var="chatMessage"> <ice:column style="width: 50px;"> <sn:friend portraitStyleClass="avatar" portraitWidth="30px" showJobTitle="false" showOnlineStatus="false" user="#{chatMessage.user}" /> </ice:column> <ice:column> <ice:outputText value="#{chatMessage.date}"> <f:convertDateTime pattern="MM/dd/yyyy hh:mm:ss a" /> </ice:outputText> <br /> <ice:outputText value="#{chatMessage.text}" /> </ice:column> </ice:dataTable> </ice:panelGroup> <ice:inputText action="#{chatRoomsBacking.addMessage(chatRoom)}" value="#{chatRoomsBacking.messageText}" /> <ice:commandButton action="#{chatRoomsBacking.addMessage(chatRoom)}" value="#{message['add-message']}" /> </ice:panelTab> </ice:panelTabSet> </ice:form> </ice:portlet> </f:view>
Also, these portlets make heavy use of Facelets Composite Components, and demonstrate how you can create new JSF components entirely with XHTML markup.
Hope to see you at one of these conferences!
Back in October 2008 at the JSFOne conference, I was interviewed by Kito Mann of JSF Central regarding Liferay and ICEfaces. The podcast and transcript were recently published at the JSF Central website:
http://www.jsfcentral.com/listings/R4013?link
Among the topics discussed are:
JSF has a simple Inversion-of-Control (IoC) container called the JSF Managed Bean Facility (MBF). Although it has a verbose XML syntax, and is not as robust as the Spring BeanFactory, PicoContainer, or the JBoss Microcontainer, the MBF does have the basics of an IoC container, and offers features like dependency injection.
When a POJO is managed by the JSF MBF, it is typically referred to as a managed-bean. But if you're going to create a maintainable JSF webapp/portlet, it is necessary to distinguish between different kinds of managed-beans. This practice will also preserve the clean separation of concerns that JSF provides by implementing the Model-View-Controller (MVC) design pattern:
Description: This type of managed-bean participates in the "Model" concern of the MVC design pattern. When you see the word "model" -- think DATA. A JSF model-bean should be a POJO that follows the JavaBean design pattern with getters/setters encapsulating properties. The most common use case for a model bean is to be a database entity, or to simply represent a set of rows from the result set of a database query.
Description: This type of managed-bean participates in the "View" concern of the MVC design pattern. The purpose of a backing-bean is to support UI logic, and has a 1::1 relationship with a JSF view, or a JSF form in a Facelet composition. Although it typically has JavaBean-style properties with associated getters/setters, these are properties of the View -- not of the underlying application data model. JSF backing-beans may also have JSF actionListener and valueChangeListener methods.
Description: This type of managed-bean participates in the "Controller" concern of the MVC design pattern. The purpose of a controller bean is to execute some kind of business logic and return a navigation outcome to the JSF navigation-handler. JSF controller-beans typically have JSF action methods (and not actionListener methods).
Description: This type of bean "supports" one or more views in the "View" concern of the MVC design pattern. The typical use case is supplying an ArrayList<SelectItem> to JSF h:selectOneMenu drop-down lists that appear in more than one JSF view. If the data in the dropdown lists is particular to the user, then the bean would be kept in session scope. However, if the data applies to all users (such as a dropdown lists of provinces), then the bean would be kept in application scope, so that it can be cached for all users.
Description: This type of bean provides some type of "utility" function to one or more JSF views. A good example of this might be a FileUpload bean that can be reused in multiple web applications.
Now... One of the main benefits in making fine distinctions like this is loose coupling. What's that you ask? Well let's first take a look at an example of tight coupling, where MVC concerns can be smashed/confused into a single managed-bean:
public class ModelAndBackingAndControllerBean { private String fullName; // model-bean property private boolean privacyRendered; // backing-bean property // model-bean getter public String getFullName() { return fullName; } // model-bean setter public void setFullName(String fullName) { this.fullName = fullName; } // backing-bean getter public boolean isPrivacyRendered() { return privacyRendered; } // backing-bean setter public void setPrivacyRendered(boolean privacyRendered) { this.privacyRendered = privacyRendered; } // backing-bean actionListener for UI support logic public void togglePrivacySection(ActionEvent actionEvent) { privacyRendered = !privacyRendered; } // controller-bean business logic public String submit() { System.out.println("fullName=" + fullName); return "success"; } }
The problem here is that the bean would have to be kept in session scope because of the model-bean property. Additionally, what if we wanted to do some unit testing with mock model data? Can't do it. So in order to fix these problems, and to promote loose coupling, we would have three separate Java classes:
public class ModelBean { private String fullName; public void setFullName(String fullName) { this.fullName = fullName; } public String getFullName() { return fullName; } } public class BackingBean { private boolean privacyRendered; public void setPrivacyRendered(boolean privacyRendered) { this.privacyRendered = privacyRendered; } public boolean isPrivacyRendered() { return privacyRendered; } public void togglePrivacySection(ActionEvent actionEvent) { privacyRendered = !privacyRendered; } } public class ControllerBean { private ModelBean modelBean; public ModelBean getModelBean() { return modelBean; } public void setModelBean(ModelBean modelBean) { // Dependency injected from the JSF managed-bean facility this.modelBean = modelBean; } public String submit() { System.out.println("fullName=" + getModelBean().getFullName()); return "success"; } }
Now that the beans are found in different classes, they can all be kept in their appropriate scopes. The model-bean can be kept in session scope, and the backing-bean and controller-bean can be kept in request scope, thus saving memory resources on the server.
Finally, we can use the dependency injection features of the JSF MBF in order to inject the model-bean into the controller-bean. This can be seen in the following WEB-INF/faces-config.xml example, where the #{modelBean} Expression Language (EL) binding is used:
<?xml version="1.0" encoding="UTF-8"?> <faces-config xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd" version="1.2"> <managed-bean> <managed-bean-name>modelBean</managed-bean-name> <managed-bean-class>myproject.ModelBean</managed-bean-class> <managed-bean-scope>session</managed-bean-scope> </managed-bean> <managed-bean> <managed-bean-name>backingBean</managed-bean-name> <managed-bean-class>myproject.BackingBean</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> </managed-bean> <managed-bean> <managed-bean-name>controllerBean</managed-bean-name> <managed-bean-class>myproject.ControllerBean</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> <managed-property> <property-name>modelBean</property-name> <value>#{modelBean}</value> </managed-property> </managed-bean> </faces-config>
The main goal of the PortletFaces project is to simplify development of JSF portlets, with an emphasis on deep integration with features of Liferay Portal. The project started about a year ago and has been sponsored by Liferay in incubation with an associated PortletFaces Message Forum at the Liferay website.
Recently, Mimacom open sourced their Edoras Framework, and has deployed their community website with Liferay Portal. In summary, the Edoras Framework is designed to simplify development of web applications using a specific application stack: JSF/ICEfaces, Spring, and JPA/Hibernate.
Mimacom's next step is to add Liferay Portal to the application stack, has made PortletFaces part of the Edoras Framework, promoting the code from incubation to the Edoras Framework's SVN repository.
Over the past year, I've taught PortletFaces in several training classes, and have helped customers use the features it provides. During that time, I've come up with ideas of how to make PortletFaces easier to use. Recently, I've dona a major refactor of PortletFaces, and recently peformed the initial checkin to the Edoras Framework SVN.
One of the pain points I found was with PortletFacesUtil. Specifically, PortletFaces was requiring that you go through the trouble of writing XML to inject it into every managed bean that you wanted to use it with. Sure, there is a PortletFacesBeanBase class that automatically provides a setPortletFacesUtil() method for you, but still, writing extra XML was a pain.
So I've refactored PortletFacesUtil so that it works more like FacesContext.getCurrentInstance(). Specifically, the functionality has been split into three layers:
// EhancedFacesContext is a wrapper around FacesContext that provides JSF-specific enhancements:public abstract class ExtFacesContext extends FacesContext
// PortletFacesContext adds Portlet API features:public abstract class PortletFacesContext extends ExtFacesContext
// LiferayFacesContext adds Liferay API features:public abstract class LiferayFacesContext extends PortletFacesContext
So what does this mean to the existing PortletFaces developer? Well, instead of injecting a PortletFacesUtil into each of your managed beans, instead, you can do this:
public class BackingBean {
LiferayFacesContext liferayFacesContext = LiferayFacesContext.getInstance();
}
So now, the Spring config file in the PortletFaces JAR will do the injection once (automatically), and the portlet developer won't have to do it manually anymore.
Many thanks to Liferay for sponsoring the incubation of PortletFaces, and many thanks to Mimacom for promoting it from incubation. Also, many thanks to community member Joel Kozikowski for all his source contributions and hours of phone calls and great advice. Joel and I will be the main PortletFaces committers at the Edoras Framework, and we'll be working to make PortletFaces an invaluable tool for JSF and ICEfaces portlet developers.
Iterator components like the JSF h:dataTable and ICEfaces ice:dataTable have two special attributes: value and var. The value attribute is typically bound to a JSF model-managed-bean, and the var attribute introduces a new variable into the JSF Expression Language (EL) like this:
<ice:dataTable value="#{modelBean.rows}" var="row"> <ice:column> <f:facet name="header"> <ice:outputText value="#{msgs.firstName}" /> </f:facet> <ice:outputText value=#{row.firstName}" /> </ice:column> ... </ice:dataTable>
The ModelBean.getRows() method can return a variety of types, but in my experience the most typical one is a generic java.util.List like this:
public List<Row> getRows() {...}
Under the hood, JSF is expecting the value attribute to contain an instance of a JSF DataModel object. In fact, if you don't return a DataModel, JSF will implicitly wrap your return value with a wrapper class, such as the ListDataModel for values of type java.util.List.
Typically, the model bean that contains the data is kept in JSF session scope or ICEfaces extended-request scope. This is fine if you only have 10 or 20 rows in the result set, but what if you have 1,000,000 rows? Obviously there is a scalability concern in such a case, especially since the user is only going to be viewing 10 or 20 rows at a time in the browser.
Unfortunately, the JSF spec does not provide a way of loading data on demand (popularly known as "lazy loading"). I've seen several posts on how to do this, but I thought it would be nice to develop a persistence-technology-independent solution.
To this end, I wrote an abstract class named LazyDataModel.java that is meant to be subclassed into a concrete implementation. Feel free to download the LazyDataModel.java (zipped) source code.
Here are the three methods you will need to supply in your concrete implementation:
public abstract int getRowsPerPage(); public abstract int countRows(); public abstract List<DTO> findRows(int startRow, int finishRow);
The DTO marker implies that this class is meant to be used at the JSF UI layer of a portlet/webapp, and that it is meant to be used in conjunction with the Data Transfer Object design pattern (formerly known as the Value Object / VO design pattern).
BTW, in order for this to be of benefit to the end user, you'll need to connect an ICEfaces ice:dataPaginator component to the ice:dataTable iterator. That will provide icons for navigation such as first-page, previous-page, next-page, and last-page.