There has been a rise in frontend frameworks, and these have competed with each other to become dominant frameworks for the web. JavaServer Faces (JSF), while it isn't the new kid on the block, still has a fairly large community and is the primary framework in the Java EE space for building UIs, which makes it a force to be reckoned with.
JSF is the standard user interface for building applications with Java EE. It takes a component-based approach to building the UI, which is different to the traditional request-based model. While it has been around for over a decade, it didn't gain much traction, arguably, until the 2.x release. There have been supporting frameworks and libraries built around JSF, and thus it enjoys good community support. Frameworks such as PrimeFaces, RichFaces, and IceFaces, along with libraries such as OmniFaces and more, have made it a popular choice among developers. That doesn't mean there aren't any critics; a framework this old is bound to have an opinionated community. With new client-side solutions making their mark, including Angular and React, the competition has only grown tougher. That's good news for developers, as it leads to a richer selection of choices for building your next web application.
The latest update of 2.3 brings with it many enhancements and refinements and makes it aligned with Java 8. Some of the major features include:
- CDI integration, which makes it easy for injecting JSF artefacts into classes and EL expressions
- The confusion of Managed Bean annotations is finally resolved
- Supports Java 8 date/time APIs
- f:websocket, which allows for easy usage of the WebSocket protocol
- Validation and conversion enhancements
- Lots of API updates and fixes
When writing a JSF application, its fairly routine to obtain references to certain context-based objects. Earlier versions didn't have any easy way to obtain these, and developers had to look up the instances by using some statically chained methods, such as the following:
FacesContext.getCurrentInstance().getExternalContext(). [ get request map, get request header map, and more get stuff]
This issue is solved by CDI, as it allows for injecting these artefacts directly in your classes. Additionally, it's also possible to use these via the EL expression. All of this is possible because JSF now provides some default providers for common use cases. A few handy ones are listed in the following table:
Before |
EL variable available |
Using Inject |
FacesContext.getCurrentInstance() |
#{facesContext} |
@Inject |
FacesContext.getCurrentInstance()
|
#{requestScope} |
@Inject |
FacesContext.getCurrentInstance() |
#{header} |
@Inject |
FacesContext.getCurrentInstance() |
#{param} |
@Inject |
It's important to note that the general reference types, such as Map or others, would require specifying a qualifier (RequestMap, HeaderMap, and so on), to assist in resolving the required type. With CDI integration support, it's also possible to inject your own custom validator and converter, too.
JSF 2.0 brought it's own set of annotations, but as soon as CDI arrived, those annotations had to be revisited. Since CDI has universal appeal in terms of managed beans, it conflicts with JSF's own annotations. It was finally decided with the 2.3 release to deprecate the JSF defined annotations in favour of the more flexible and universal CDI annotations. Thus, Managed Bean annotations were deprecated in favor of CDI annotations.
There's support for Java 8 date/time APIs in the 2.3 release, with an update to the existing converter tag, called <f:convertDateTime>. The type attribute now takes more values along with earlier ones, such as both, date, time, localDate, localTime, localDateTime, offsetTime, offsetDateTime, and zonedDateTime. If we have a bean with a LocalDate property, then the same can be referenced in the facelets view, as follows:
<h:outputText value="#{ticketBean.createdDate}">
<f:convertDateTime type="localDate" pattern="MM/dd/yyyy" />
</h:outputText>
The WebSocket protocol offering full bi-directional communication support, as well as developers wanting to utilize these abilities, has led to the inclusion of web socket integration in JSF standard. It's now possible to register a WebSocket with the client using the f:websocket tag, pushing messages to the client from the server using PushContext. You can get this running with very little code; all you need to do is name the channel, which is a required attribute for this tag, and then register a JavaScript callback listener through the onmessage attribute. That's it for the client side. This callback would be invoked once the server sends a message to the client. In case you are wondering, the message is encoded as JSON and sent to the client. Here are a few snippets to help you understand this better.
This is the JSF view part, which registers the WebSocket:
<f:websocket channel="jsf23Channel" onmessage="function(message){alert(message)}" />
Then, on the server side, the PushContext is injected and later used for sending the push messages to the client:
@Inject @Push
private PushContext jsf23Channel;
public void send() {
jsf23Channel.send("hello websocket");
}
A few other enhancements include support for importing constants for page authors using the <f:importConstants/> tag. Also, there will be support for the c:forEach method of iteration using ui:repeat. While we are on the iterating point, it's worth mentioning that support for map-based iteration has also been added, and this means you can now use ui:repeat, c:forEach, and h:dataTable to iterate over the entries in a map. The @FacesDataModel annotation allows for supplying your own custom registrable DataModel objects that can then be used in ui:repeat or h:dataTable. This can be utilized by library providers to add more flexibility to their components. An example of ui:repeat using a map is shown here:
<ui:repeat var="anEntry" value="#{ticketMapOfFeatures}">
key: #{anEntry.key} - value: #{anEntry.value}
</ui:repeat>
AJAX method calls are now supported—you can invoke a JavaScript method which in turn will invoke a server-side bean method in an Ajax call. Those familiar with the PrimeFaces p:remoteCommand can relate to this feature, with the difference being that it's included as a standard now. This can be done using the h:commandScript component tag. Similar to invoking the server-side code from JavaScript, you can also invoke JavaScript from server-side code as well, which is made possible using API enhancement. This is done by referencing the PartialViewContext and invoking the getEvalScripts method for adding your JavaScript code to the response. With so many additions, JSF has once again become worth adding to a developers arsenal when building web applications for Java EE.