Friday, 17 October 2014

JavaServer Faces (JSF) Tutorial

Preface

JavaServer Faces, aka JSF, is a client-server framework for building Java-based web applications - a framework for developing web applications, and wiring their client-side user interface with the server-side.

Although there are many tutorials about JSF, many developers and architects disqualify the JSF framework. We give in here some common complaints and then argue them:

  • How does JSF co-exist with the popular MVC design-pattern?
    • JSF represents only one part of the MVC design-pattern - the View. The view part of MVC is a complex issue by itself and is built from both client-side code and server-side code and the interaction between them.
  • I had a bad experience with JSF. It did not give me an adequate solution to client-server interaction. I needed to use several third-party libraries and still I did not get a qualified solution to the client-server challenge as I expected.

    • It is true that older versions of JSF lack many aspects of an adequate client-server framework. There was a massive use of several third-parties libraries and building an infrastructure with many libraries that co-exist with each other and use their matching versions in order to work together was a very hard thing to do. But still, this was the best choice for the java community for several years.

      We are glad to announce that since JSF 2.0 there is an amazing and continuance improvement on the JSF framework. It is a much more reacher framework than before and supports up-to-date aspects of client-server challenges, such as AJAX, JSONP, File-Upload, HTML5, CSS3 and more. A JSF framework does not need anymore third-party libraries, because it is comprehensive enough.

  • While JSF tries to introduce a framework for HTML client-server interaction, it displays a poor HTML solution. It has only basic HTML components that does not compete with evolving market and HTML challenges.

    • The JSF framework comes with a default render-kit for rendering HTML markup, that is tightly bound to server-side Java-Beans. While it gives a great solution for simple HTML components, like radio buttons, input text, forms and selectors, you still need to use HTML libraries in order to make your site amazing.

      This does not reduce from its ability to ease the clent-server interaction. You can still use the default render-kit components effectively with another HTML widget library that can wrap the JSF components. Notice that there is a separation of activities here. The rendering of the JSF components happens on the server side, and the wrapping of the JSF components with an happens on the client side, and to be more specific, on the web browser.

      We recommend to use Kendo UI HTML5 Widget Library. It gives a very rich controls for the browser and works seamlessly with JSF.

  • It is lacking the separation of responsibilities between client-side developement and server-side developement.

    • Although JSF is written both in the client-side and in the server-side, it represents only one part of the MVC deign-pattern - the View. It is up to the organization if to refer to JSF technology as a server-side framework or client-side framework. In most cases it is developed by client-side developers that also write java code, while yje latter also interacts with other layers of the application such as the Model layer and Controller layer.

Terminology

  • JSF Component - is a JSF code idiom, which is built of a collection of server-side code and client-side code that works together in order to perform or represent a specified functionality. The JSF framework manages the life-cycle of this code and is also responsible for the rendering of the client-side code. Examples are:

    • A client-side form controller that sends its elements to the server and automatically populates a java bean on the server side
    • A client-side table / grid controller that is automatically synchronized with its server-side representation

Main Features

The main features of JavaServer Faces are:

  • A GUI Component Framework - the browser's HTML tags are tightly coupled with the server code, as a set of Java classes
  • Different markup languages - a flexible model for rendering components in different markup languages, such as HTML, SVG, XUL, or even create your own custom render kit
  • a javax.faces.render.Renderer class that translates JSF components into browser's markup code and delegates HTTP requests and responses between the server and the web browser. E.g.:
    • JSF form components (h:form) to HTML "form" tags
    • JSF table components (h:datatable) to HTML "table" tags
    • JSF drop-down / select-box components (such as h:selectOneMenu) to HTML "select" tags
    • JSF checkbox components to HTML input type="checkbox" tags
    • JSF Container components (such as h:panelGroup) to HTML "div" tags
    • etc...
  • A standard javax.faces.render.RenderKit for generating HTML markup.

Complementary GUI Components features

The following features support the GUI components:

  • Input validation - validation of input fields received by an HTTP request from the browser
  • Event handling - attaching listeners to different browser events, like changing input field, change in a drop-down and so forth
  • Data conversion - The framework automatically casts from strings received from the HTTP request and their corresponding type on the server-side. E.g. "1" may be cast to int, 1.5 to float.
  • Managed object-model life-cycle - The JSF object life-cycle are managed by the framework. It supplies an API for:
    • Representing components and managing their state
    • Handling events, server-side validation, and data conversion
    • Defining page navigation
    • Supporting internationalization and accessibility
    • Providing extensibility for all these features
  • Page navigation configuration - The navigation to application screens may be configured and controlled easily
  • Expression Language (aka EL) - using expressions that map directly to server objects
  • Tag libraries - Using JSF tag and EL tags on web pages to represent JSF components

Benefits of JSF Infrastructure

  • Drop components onto a web page by adding component tags.
  • Bind components on a page to server-side data.
  • Wire component-generated events to server-side application code.
  • Save and restore application state beyond the life of server requests.
  • Reuse and extend components through customization.

New JSF Features

The Java EE 6 platform contains implementations of JavaServer Faces version 2.0 and Expression Language version 2.2. These implementations adds a lot of functionality and makes the developement of JSF applications much easier. The new features are:

  • JSF annotations - The ability to use JSF annotations instead of a configuration file in order to specify managed beans and other components
  • Facelets - a display technology that uses templates and analyzes XHTML syntax files instead of JavaServer Pages (aka JSP) files. By using XHTML the JSF render kit engine can better analyze the files and support more features.
  • Ajax support
  • Composite components - the ability to write your own components using the same template engine and render kit
  • Implicit navigation - Using navigation results with the page navigation feature directly inside the JSF markup

JSF Version History (for Infrastructure Development and Architects)

When you download the JEE developement kit it comes with all the implementations and their correct versions that JSF and other resources need. While this is a very simple task to do, in most cases you will not want all the JEE SDK to be included in your distribution and run on your server, therefore, you usually prefer to get just what you need. In order to do so you need to work what are the pieces that work together. Here is a short summary of some of the JSF building blocks:

J2EE 1.4

Java Servlet 2.4 JSR154
JavaServer Pages (JSP) 2.0 JSR152
JavaServer Pages Standard Tag Library (JSTL) 1.1 JSR52
JavaServer Faces (JSF) 1.1 JSR127

Java EE 5

Java Servlet2.5JSR154
JavaServer Faces (JSF)1.2JSR252
JavaServer Pages (JSP)2.1JSR245
JavaServer Pages Standard Tag Library (JSTL)1.2JSR52
Debugging Support for Other Languages1.0JSR45

Java EE 6

Java Servlet3.0JSR315
JavaServer Faces (JSF)2.0JSR314
JavaServer Pages (JSP)2.2JSR245
Expression Language (EL)2.2JSR245
JavaServer Pages Standard Tag Library (JSTL)1.2JSR52
Debugging Support for Other Languages1.0JSR45

Java EE 7

Java API for WebSocketJSR356
Java API for JSON ProcessingJSR353
Java Servlet3.1JSR340
JavaServer Faces (JSF)2.2JSR344
Expression Language (EL)3.0JSR341
JavaServer Pages (JSP)2.3JSR245
JavaServer Pages Standard Tag Library (JSTL)1.2JSR52

JSF-2 Scopes

JSF2 offers six predefined @ManagedBean scopes. Their lifetime and use are described in detail below:

  • @RequestScoped
    • Lives – between the browser's submission of a request to the server and until the response was delivered back to the browser.
    • Storing and Retrieval
      • JSF stores the bean as an attribute of HttpServletRequest with the managed bean name as key
      • It is also available by ExternalContext#getRequestMap()
    • Usage - Use this scope for pure request-scoped data.
  • @ViewScoped
    • Lives - From an HTTP request for the specified view. It stays alive as long as you're interacting with the same JSF view ("f:view") and until there is a request to a different view.
    • Storing and Retrieval
      • JSF stores the bean in the UIViewRoot#getViewMap(). The map itself is stored in the session.
      • It is also available by invoking ExternalContext#getRequestMap()
    • Usage - Use this scope for complex forms that their state needs to be retained in subsequent requests.
    • This also answers the multiple browser's tab issue. While the session scope is shared between multiple browser tabs, the view scope is not shared.
  • FlashScoped - there is no annotation for the flash scope.
    • Lives - the flash scope lives between views. In other words, it is a way for JSF views to make conversations with each other.
    • Storing and Retrieval
      • You can retrieve the Flash Scope by invoking the following:
        (FacesContext.getCurrentInstance().getExternalContext().getFlash())
      • Then, you can populate the flash by invoking:
        flashRetrieved.put("bean", this);
      • To read the flash from the next view you can write:
        #{flash.bean.text}
        or
        #{flash['bean'].text}
    • Usage - Use this scope for HTML forms (on different views) that need to interact with each other.
  • @SessionScoped
    • Lives - It is created upon the first HTTP request involving this bean in the session, and lives as long as the HTTP session lives. This means that if the session is invalidated or the bean is manually removed from the session, the bean will not live any more.
    • Storing and Retrieval - It is stored on the HttpSession. It can be retrieved by invoking ExternalContext#getSessionMap().
    • Usage - For session-scoped data which can safely be shared among windows and tabs (views) from the same session.
  • @ApplicationScoped
    • Lives - It is created upon the first HTTP request involving this bean in the application, or when the web application starts with the "eager=true" attribute on the managed-bean. It lives as long as the web application lives, or while it was not manually removed from application map.
    • Storing and Retrieval - on the ServletContext
    • Usage - For pure application-scoped data which can safely be shared among all sessions.
  • @NoneScoped
    • Lives - It is created on each and every EL evaluation and destroyed right after the end of the EL evaluation.
    • Storing and Retrieval - JSF does not store the bean anywhere.
    • Usage - Use this scope on beans that shhould behave only as data beans and need to be injected in another scoped-bean (AcceptorBean). The injected bean will then live as long as the acceptor bean. The advantage of using @noneScope is that you can use it as a @ManagedProperty in any bean of any scope.
  • @CustomScoped
    • Lives - Custom implemented.
    • Usage - if no one of the other scopes suits your requirements.

Processing GET request parameters

JSF 2.x you can use the "f:viewParam" tag, inside "f:metadata", to add functionality to GET request parameters. You can attach to them Converters, Validators, Messages and Events:

Eg-1

<f:metadata>
    <f:viewParam name="id" value="#{bean.userId}"/>
    <f:event type="preRenderView" listener="#{bean.init}"/>
</f:metadata>

Eg-2

<f:metadata>
        <f:viewParam name="id" value="#{bean.user}"
            converter="#{userConverter}"
            converterMessage="Bad request. Unknown user."
            required="true"
            requiredMessage="Bad request. Unsupported link."
        />
</f:metadata>

Eg-3

<f:metadata>
        <f:viewParam name="id" value="#{bean.user}"
            converterMessage="Bad request. Unknown user."
            required="true"
            requiredMessage="Bad request. Unsupported link."
        />
</f:metadata>

Eg-4

<f:metadata>
        <f:viewParam name="id" value="#{users.user}"
            converter="#{userConverter}"
            converterMessage="Bad request. Unknown user."
            required="true"
            requiredMessage="Bad request. Unsupported link."
        />
</f:metadata>

Managing Bean Lifecycle

You can react to the creation and the destruction of a managed bean, as follows:

public class MyBean {

  @PostConstruct
  public void postCreate(){
    ...
  }

  @PreDestroy
  public void preDestroy(){
    ...
  }

To make things simpler, you can create an abstract class with these methods and extend it on managed-beans.

Implicit EL objects

JSF2 EL comes with several implicit EL objects:

  • #{component}: the current javax.faces.component.UIComponent
  • #{facesContext}: the current javax.faces.context.FacesContext
  • #{view}: the current javax.faces.component.UIViewRoot
  • #{request}: the current javax.servlet.http.HttpServletRequest
  • #{session}: the current javax.servlet.http.HttpSession
  • #{application}: the javax.servlet.ServletContext
  • #{flash}: the current javax.faces.context.Flash
  • #{cc}: the current Composite Component
  • #{requestScope}: the current request attribute map
  • #{viewScope}: the current view attribute map
  • #{sessionScope}: the current session attribute map
  • #{applicationScope}: the application attribute map
  • #{initParam}: the current context parameter map
  • #{param}: the current request parameter map
  • #{paramValues}: the current request parameter values map
  • #{header}: the current request header map
  • #{headerValues}: the current request header values map
  • #{cookie}: the current request cookie map
  • #{resource}: converts a JSF resource identifier to a concrete resource URL.

Referring to the current component with EL

You can use the #{component} EL object to reference the current component, like the keyword of this is used in Java classes:

<h:inputText value="#{bean.value}"
    styleClass="#{component.valid ? '' : 'error'}"/>

Adding Ajax Capabilities

The f:ajax tag

The f:ajax tag adds Ajax capabilities to one or more JSF components. It can be nested within a single UI component to enable Ajax for that component, or it can wrap around multiple components to enable Ajax for many components.

Resolving render (Re-render) IDs

Render IDs are resolved relative to the parent UINamingContainer (javax.faces.component.UINamingContainer) component. Examples of such components are h:form, h:dataTable, ui:repeat, etc. These components prepends their own IDs to the client ID of their children.

The following idiom will not work:

<h:form id="form">
        <h:commandButton value="Submit">
            <f:ajax render="result" />
        </h:commandButton>
</h:form>

<h:outputText id="result" value="#{bean.result}" />

Since the "result" ID is not in the UINamingContainer where the ajax command resides, it will not be found by it.

If you prefix the ID to render with a colon (":") it will be resolved relative to the view root. So to fix the previous idiom we should write:

<h:form id="form">
    <h:commandButton value="Submit">
        <f:ajax render=":result" />
    </h:commandButton>
</h:form>

<h:outputText id="result" value="#{bean.result}" />

Adding a colon tells the ajax tag to search for an absolute render ID, so if the id to be rendered resides in another UINamingContainer, we need to include its ID as well:

    <h:form id="form">
        <h:commandButton value="Submit">
            <f:ajax render=":otherform:result" />
        </h:commandButton>
    </h:form>

    <h:form id="otherform">
        <h:outputText id="result" value="#{bean.result}" />
    </h:form>

Ajax rendering of content outside form

See "Resolving render (Re-render) IDs"

Ajax rendering of content which contains another form

When you ajax render a content which contains another form, then the other form will render but it will loose some of the information that it needs in order to communicate with the JSF framework.

The following idiom will not work:

<h:panelGroup id="firstPanel">

    <h:form id="firstForm">
        <h:outputLabel for="input" value="First form input" />
        <h:inputText id="input" value="#{bean1.input}" required="true" />
        <h:commandButton value="Submit form" action="#{bean1.submit}">
            <f:ajax execute="@form" render="@form :secondPanel :messages" />
        </h:commandButton>
        <h:message for="input" />
    </h:form>

</h:panelGroup>

<h:panelGroup id="secondPanel">

    <h:form id="secondForm">
        <h:outputLabel for="input" value="Second form input" />
        <h:inputText id="input" value="#{bean2.input}" required="true" />
        <h:commandButton value="Submit other form" action="#{bean2.submit}">
            <f:ajax execute="@form" render="@form :firstPanel :messages" />
        </h:commandButton>
        <h:message for="input" />
    </h:form>

</h:panelGroup>

To get it to work you need to Explicitly add the client ID of the other form in the f:ajax render:

<h:panelGroup id="firstPanel">

    <h:form id="firstForm">
        <h:outputLabel for="input" value="First form input" />
        <h:inputText id="input" value="#{bean1.input}" required="true" />
        <h:commandButton value="Submit form" action="#{bean1.submit}">
            <f:ajax execute="@form"
                    render="@form :secondPanel :secondForm :messages" />
        </h:commandButton>
        <h:message for="input" />
    </h:form>

</h:panelGroup>

<h:panelGroup id="secondPanel">

    <h:form id="secondForm">
        <h:outputLabel for="input" value="Second form input" />
        <h:inputText id="input" value="#{bean2.input}" required="true" />
        <h:commandButton value="Submit other form" action="#{bean2.submit}">
            <f:ajax execute="@form"
                    render="@form :firstPanel :firstForm :messages" />
        </h:commandButton>
        <h:message for="input" />
    </h:form>

</h:panelGroup>

Ajax rendering of content which is by itself conditionally rendered

Elements that are conditionally rendered, may not be available to the f:ajax component, because the element to be rendered on the Ajax request may not be available on the page. For example, in the following idiom, if "renderResult" is false while the page is rendered, the "result" component, which is conditionally rendered, will not be available on the page, and thus will not be available to the "f:ajax" component:

The following idiom will not work when #{bean.renderResult} defaults to false:

<h:form id="form">
    <h:commandButton value="Submit">
        <f:ajax render=":result" />
    </h:commandButton>
</h:form>

<h:outputText id="result" rendered="#{bean.renderResult}" />

For this to work you need to ensure that you give the "render" attribute of the "ajax" component an ID that points to a component which is already present in the output. So we wrap the component to be rendered in a panel:

<h:form id="form">
    <h:commandButton value="Submit" action="#{bean.toggleRenderResult}">
        <f:ajax render=":result" />
    </h:commandButton>
</h:form>

<h:panelGroup id="result">
    <h:outputText value="#{bean.result}" rendered="#{bean.renderResult}" />
</h:panelGroup>

Common problems

  • Merge JSF screens with new design from a designer
    • It is a common problem for JSF developers, when their site need to be upgraded with a new design. They receive an HTML from a designer and need to integrate it in their existing, fully-functional JSF application.

      There are several ways to do that. As we experienced this problem we came to the following conclusions:

      • One may choose to take the code from the designer and then change the JSF code to match the markup code from the designer. We found that it is a critical mistake. JSF deals with both design and functionality while the designer's code deals only with design. To match a fully-functional existing code with a third-party code that deals only with the design is a very hard task. We don't want to change the functionality of our application for this.
      • A better way may be to match the new design to the existing JSF code, which is already functional. Changing code that deals with design issues only is a much simpler task than changing code that deals with functionality.

No comments:

Post a Comment