Home Business & Other Mastering JavaServer Faces 2.2

Mastering JavaServer Faces 2.2

By Anghel Leonard
books-svg-icon Book
eBook $39.99 $27.98
Print $65.99
Subscription $15.99 $10 p/m for three months
$10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
BUY NOW $10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
eBook $39.99 $27.98
Print $65.99
Subscription $15.99 $10 p/m for three months
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
  1. Free Chapter
    Dynamic Access to JSF Application Data through Expression Language (EL 3.0)
About this book
Publication date:
June 2014
Publisher
Packt
Pages
578
ISBN
9781782176466

 

Chapter 1. Dynamic Access to JSF Application Data through Expression Language (EL 3.0)

Java Expression Language (EL) is a compact and powerful mechanism that enables dynamic communication in JSP and JSF-based applications (including development frameworks based on JSF such as PrimeFaces, ICEfaces, and RichFaces); we embed expressions in the presentation layer to communicate with the application logic layer. EL provides bidirectional communication, which means that we can expose application logic data to the user, but we also can submit user data to be processes. Generically speaking, EL can be used to populate HTTP requests with user data, to extract and expose data from HTTP responses, to update HTML DOM, to conditionally process data, and much more.

Note

Commonly, EL expressions will be present in JSP and JSF pages, but they can also appear outside, in faces-config.xml, for example.

In this chapter, you will see how to use EL in web pages to communicate with managed beans, which is the most common case in JSF applications. We will cover the following topics:

  • EL syntax, operators, and reserved words

  • EL immediate and deferred evaluation

  • EL value and method expressions

  • The conditional text in JSF

  • Write a custom EL resolver

 

EL syntax


In this section, you can see an overview of the main aspects of EL 2.2 and 3.0 syntax. EL supports a handful of operators and reserved words. Each of these are quickly described in the following section (more details are in the EL specification document (http://download.oracle.com/otndocs/jcp/el-3_0-fr-eval-spec/index.html)).

EL operators

EL supports the following categories of operators—arithmetic, relational, logical, conditional, empty and added starting with EL 3.0, string concatenation, assignment and semicolon operators:

Textuals

Description

Symbols

A + B

Addition

+

A - B

Subtraction

-

A * B

Multiplication

*

A {div, /} B

Arithmetic operator division

/, div

A {mod, %} B

Arithmetic operator modulo

%, mod

A {and, &&} B

Logical AND

&&, and

A {or, ||} B

Logical OR

||, or

{not, !} A

Logical opposite

!, not

A {lt, <} B

Relational less than

<, lt

A {gt, >} B

Relational greater than

>, gt

A {le, <=} B

Relational less than or equal to

<=, le

A {ge, >=} B

Relational greater than or equal to

>=, ge

A {eq, ==} B

Equal to

==, eq

A {ne, !=} B

Not equal to

!=, ne

A = B

Assignment (EL 3.0)

=

A ; B

Semicolon (EL 3.0)

;

A += B

String concatenation (EL 3.0)

+=

A -> B

Lambda expression (EL 3.0)

->

empty A

Determine whether a value is null or empty

 

A ? B : C

Evaluates B or C, depending on the result of the evaluation of A. Known as the ternary operator.

?:

 

Used when writing EL expressions

.

 

Used when writing EL expressions

[]

EL precedence of operators

Conforming to EL specification, the precedence of operators from the highest to lowest, left to right is as follows:

  • [].

  • () (used to change the precedence of operators)

  • - (unary) not ! empty

  • * / div % mod

  • + - (binary)

  • +=

  • < > <= >= lt gt le ge

  • == != eq ne

  • && and

  • || or

  • ? :

  • -> (lambda expression)

  • =

  • ;

EL reserved words

EL defines the following reserved words:

  • and, or, not, eq, ne, lt, gt, le, ge, true (Boolean literal), false (Boolean literal), null, instanceof (a Java keyword to do a class comparison between objects), empty, div, and mod

 

EL immediate and deferred evaluation


EL evaluates expressions as immediate or deferred.

Immediate evaluation returns the result as soon as the page is first rendered. These kinds of expressions are read-only value expressions and they can be present only in tags that accept runtime expressions. They are easy to recognize after the ${} notation. Usually, they are used for arithmetic and logical operations in JSP pages.

Deferred evaluation can return the result at different phases of a page's life cycle depending on the technology that is using the expression. JSF can evaluate the expression at different phases of the life cycle (for example, during the rendering and postback phase), depending on how the expression is being used in the page. These kind of expressions can be value and method expressions, and they are marked by the #{} notation.

Note

In Facelets, ${} and #{} act the same.

 

EL value expressions


Value expressions are probably used the most, and they refer to objects and their properties and attributes. Such expressions are dynamically used to evaluate results or set bean properties at runtime. Through value expressions, you can easily access JavaBeans components, collections, and Java SE enumerated types. Moreover, EL provides a set of implicit objects that can be used to get attributes from different scopes and parameter values. Furthermore, you will see how EL deals with each of these objects.

Note

Value expressions that can read data, but cannot write it are known as rvalue (${} expressions are always rvalue), while those that can read and write data are known as lvalue (#{} expressions can be rvalue and/or lvalue).

Referencing a managed bean

Referencing a managed bean is not exactly a useful example, but it is a good point to start. Most commonly, your managed bean will look like the following code (in this case, the bean's class name is PlayersBean):

@ManagedBean
//some scope
public class PlayersBean{
...
}

Or, in the CDI version, your managed bean will be as follows:

@Named
//some scope
public class PlayersBean{
...
}

Or, with an explicit name, your managed bean will be as follows:

@ManagedBean(name = "myPlayersBean")
//some scope
public class PlayersBean{
...
}

@Named(value = "myPlayersBean")
//some scope
public class PlayersBean{
...
}

Now, for the first two examples, EL refers to the PlayersBean managed bean, like this—the name is obtained from taking the unqualified class name portion of the fully qualified class name and converting the first character to lowercase as follows:

#{playersBean}

In addition, for the next two examples, EL uses the explicit name as follows:

#{myPlayersBean}

Note

You should use CDI beans whenever possible since they are more flexible than JSF managed beans, and because annotations from javax.faces.bean will be deprecated in a future JSF version. Therefore, the CDI ones are recommended.

When the referenced managed bean cannot be found in any scope, a null value will be returned.

Tip

Downloading the example code

You can download the example code files for all Packt books you have purchased from your account at http://www.packtpub.com. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

Referencing a managed bean's properties

As is commonly known, managed beans usually contain private fields, which are accessible through getter and setter methods as bean properties, and some public methods that exploits these properties to serve different logic tasks.

EL expressions that can access these properties contain the dot or square brackets notation, []. For example, let's suppose that the PlayersBean managed bean contains two fields defined like the following lines:

private String playerName = "Rafael";
private String playerSurname = "Nadal";

EL can access these fields through their getter methods; therefore, you need to define them as shown in the following code:

public String getPlayerName() {
  return playerName;
}
public String getPlayerSurname() {
  return playerSurname;
}

Now, an expression that accesses the playerName property can use the dot notation (.) to refer it, as shown in the following line of code:

#{playersBean.playerName}

Alternatively, this expression can use the square brackets notation, [], as shown in the following line of code:

#{playersBean['playerName']}

Note

JSF evaluates this expression from left to right. First, it searches for playersBean in all available scopes (such as request, session, and application). Then, the bean is instantiated and the getPlayerName/getPlayerSurname getter methods are called (in the case of Boolean properties, the getter method will be named as isXXX).When you are using the [] notation, you can use simple or double quotes. Just remember to alternate them correctly in cases like the following quotations.

An incorrect quotation (you cannot use double quotes inside double quotes) is:

<h:outputText value="#{playersBean["playerName"]}"/>

An incorrect quotation (you cannot use simple quotes inside simple quotes) is:

<h:outputText value='#{playersBean['playerName']}'/>

A correct quotation (you can use simple quotes in double quotes) is:

<h:outputText value="#{playersBean['playerName']}"/>

A correct quotation (you can use double quotes in simple quotes) is:

<h:outputText value='#{playersBean["playerName"]}'/>

Referencing a managed bean's nested properties

Usually, managed beans use nested properties. Such properties can be accessed by EL using the . and [] notations multiple times in the same expression.

For example, the PlayersBean managed bean may represent general data about tennis players, such as name, surname, titles, and finals. More detailed information, such as birthday, birthplace, height, and weight can be represented through a different class named PlayersDetails. Now, the PlayersBean managed bean contains a field of type PlayersDetails, which means that birthday, birthplace, and so on become nested properties of PlayersBean. Speaking in code lines, the relevant part of the PlayersDetails class is as follows:

public class PlayerDetails {

  private Date birthday;
  private String birthplace;
  ...

  public Date getBirthday() {
    return birthday;
  }

  public String getBirthplace() {
    return birthplace;
  }
  ...
}

The managed bean of the PlayersBean class is as follows:

@Named
public class PlayersBean{

  private String playerName = "Rafael";
  private String playerSurname = "Nadal";
  private PlayerDetails playerDetails;

  public String getPlayerName() {
    return playerName;
  }

  public String getPlayerSurname() {
    return playerSurname;
  }

  public PlayerDetails getPlayerDetails() {
    return playerDetails;
  }
  ...
}

You already know how to call the playerName and playerSurname properties using the . and [] notations. Next, you can use the same notations to access the birthday and birthplace nested properties, as shown in the following code:

#{playersBean.playerDetails.birthday}
#{playersBean.playerDetails.birthplace}

#{playersBean['playerDetails']['birthday']}
#{playersBean['playerDetails']['birthplace']}

Or, you can use both notations in the same expressions, as shown in the following code:

#{playersBean.playerDetails['birthday']}
#{playersBean.playerDetails['birthplace']}

#{playersBean['playerDetails'].birthday}
#{playersBean['playerDetails'].birthplace}

Of course, the PlayerDetails class can contain its own nested properties and so. In this case, just use the . and [] notations to get deeper in the hierarchy of objects until you reach the desired property.

In the preceding expressions, JSF search for playersBean in all the available scopes (request, session, application, and so on) and obtain an instance of it. Afterwards, it calls the getPlayerDetails method and the getBirthday method on result of the getPlayerDetails method (and the same for the birthplace property).

Referencing Java SE enumerated types

EL can access Java SE enumerated types using a String literal. For example, let's have an enumerated type defined in PlayersBean, as shown in the following code:

public enum Plays {
  Left, Right
};

private Plays play;
...
play = Plays.Left;//initialization can be done in constructor
...
public Plays getPlay() {
  return play;
}
...

You can easily output the play value as shown in the following line of code:

#{playersBean.play}

To refer to the Plays constant, Plays.Left, with an expression, use the String literal Left (or Right for Plays.Right), for example, you can test whether play is Left or Right, as shown in the following code:

#{playersBean.play == 'Left'} //return true
#{playersBean.play == 'Right'}//return false

Referencing collections

Collection items (arrays, lists, maps, sets, and so on) can be accessed from EL expressions by specifying a literal value that can be converted to an integer or the [] notation with an integer and without quotes.

For example, let's suppose that the PlayersBean managed bean contains an array named titles_2013 that keeps the titles won by a player in 2013. The array is defined as shown in the following code:

private String[] titles_2013 = {"Sao Paulo", "Acapulco", "ATP World Tour Masters 1000 Indian Wells", "Barcelona", ...};
...
public String[] getTitles_2013() {
  return titles_2013;
}   

Now, you can access the first title from the array by specifying its position in array, which is 0:

#{playersBean.titles_2013[0]}

This is equivalent in Java to getting or setting the value for titles_2013[0].

However, sometimes you need to iterate over the array instead of accessing a specific item. This can be easily accomplished with the c:forEach JSTL tag (http://www.oracle.com/technetwork/java/index-jsp-135995.html). The following code snippet iterates over the titles_2013 array and outputs each item (this is a pretty uncommon usage, so do not try it in production):

<c:forEach begin="0" 
  end="${fn:length(playersBean.titles_2013)-1}" 
  var="i">
  #{playersBean.titles_2013[i]},
</c:forEach>

You can simplify it as shown in the following code:

<c:forEach var="title" items="#{playersBean.titles_2013}">
  <i>#{title}</i>,
</c:forEach>

You can also use the <ui:repeat> tag as shown in the following code:

<ui:repeat var="title" value="#{playersBean.titles_2013}">
  <i>#{title}</i>,
</ui:repeat>

This tag is detailed in Chapter 12, Facelets Templating, in the Iterating with <ui:repeat> section.

You can use the same approach for every List. For example, in the case of List, the expression #{playersBean.titles_2013[0]} is equivalent in Java to titles_2013.get(0) and titles_2013.set(0, some_value).

In the case of collections of type key-value (for example, Map), the EL expressions obtain items by key. For example, let's add a Map in PlayersBean that stores some match facts of a player. It can be defined as shown in the following code:

private Map<String, String> matchfacts = new HashMap<>();
...
matchfacts.put("Aces", "12");
matchfacts.put("Double Faults", "2");
matchfacts.put("1st Serve", "70%");
...

public Map<String, String> getMatchfacts() {
  return matchfacts;
}

Now, an EL expression that accesses the item with the key Aces can be written like the following line of code:

#{playersBean.matchfacts.Aces}

Note

Notice that this approach is not supported on arrays or lists. For example, #{playersBean.titles_2013.0} is not correct.

When the key is not an acceptable variable name (for example, Double Faults), you need to use brackets and quotes, as shown in the following code:

#{playersBean.matchfacts["Double Faults"]}

EL implicit objects

JSF provides several objects related to the current request and environment. EL exposes these objects (known as implicit objects) that can be accessed at runtime in a Facelet, servlets, or backing bean—these objects are accessible through value expressions and are managed by the container. For each expression, EL first checks if the value of the base is one of these implicit objects, and, if it is not, then it will check beans in progressively wider scopes (from request to view, and finally to application scope).

Note

In EL, the part of the expression before the dot or the square bracket is named base and it usually indicates where the bean instances should be located. The part after the first dot, or the square bracket, is called a property and is recursively cracked in smaller parts, which represents the bean's properties to get from the base.

You can see a short overview of these objects in the following table:

Implicit object EL

Type

Description

#{application}

ServletContext or PortletContext

This is an instance of ServletContext or PortletContext.

#{facesContext}

FacesContext

This is an instance of FacesContext.

#{initParam}

Map

This is the context initialization parameter map returned by getInitParameterMap.

#{session}

HttpSession or PortletSession

This is an instance of HttpSession or PortletSession.

#{view}

UIViewRoot

This is the current UIViewRoot (the root of the UIComponent tree).

#{component}

UIComponent

This is the current UIComponent.

#{cc}

UIComponent

This is the composite component currently being processed.

#{request}

ServletRequest or PortletRequest

This is an instance of ServletRequest or PortletRequest.

#{applicationScope}

Map

This is a map to store application-scoped data returned by getApplicationMap.

#{sessionScope}

Map

This is a map to store session-scoped data returned by getSessionMap.

#{viewScope}

Map

This is a map to store current view scoped data returned by getViewMap.

#{requestScope}

Map

This is a map to store request-scoped data returned by getRequestMap.

#{flowScope}

Map

This is a map to store flow-scoped data returned by facesContext.getApplication().getFlowHandler().getCurrentFlowScope().

#{flash}

Map

This is a map that contains values present only on the "next" request.

#{param}

Map

This is a map view of all the query parameters for this request. It is returned by getRequestParameterMap.

#{paramValues}

Map

This is the request parameter value map returned by getRequestParameterValuesMap.

#{header}

Map

This is a map view of all the HTTP headers for this request returned by getRequestHeaderMap.

#{headerValue}

Map

This is the request header values map returned by getRequestHeaderValuesMap. Each value in the map is an array of strings that contains all the values for that key.

#{cookie}

Map

This is a map view of values in the HTTP Set-Cookie header returned by getRequestCookieMap.

#{resource}

Resource

This is a JSF resource identifier to a concrete resource URL.

 

EL method expressions


With EL expressions, we can call arbitrary static and public methods that live on the server side in managed beans. Such expressions are usually present in tag's attributes (that is, inside an action or actionListener attribute) and must use the deferred evaluation syntax since a method can be called during different phases of the life cycle. Commonly, methods are called to respond with actions to different kinds of events and for autopages navigation.

Let's see some examples of calling bean methods using EL (all methods were defined in the PlayersBean managed bean):

  • Calling the vamosRafa_1 void bean method with no arguments, as shown in the following code:

    public void vamosRafa_1(){
      System.out.println("Vamos Rafa!");
    }
    
    #{playersBean.vamosRafa_1()}
  • Calling the vamosRafa_2 bean method with no arguments. It returns a string, as shown in the following code:

    public String vamosRafa_2() {
      return "Vamos Rafa!";
    }
    
    #{playersBean.vamosRafa_2()}

    The returned string, Vamos Rafa!, can be displayed on the web page or used for other purposes. In other words, the expression will be evaluated to this string.

  • Calling the vamosRafa_3 bean method with one argument. It returns void, as shown in the following code:

    public void vamosRafa_3(String text) {
      System.out.println(text);
    }
    
    #{playersBean.vamosRafa_3('Vamos Rafa!')}

    Notice that the String arguments are passed by using quotes.

    Note

    The String constants are passed between simple or double quotes!

  • Calling the vamosRafa_4 bean method with two arguments. It returns a string, as shown in the following code:

    public String vamosRafa_4(String name, String surname) {    
      return "Vamos " + name + " " + surname + "!";
    }
    
    #{playersBean.vamosRafa_4(playersBean.playerName, playersBean.playerSurname)}

    The expression will be evaluated to the string, Vamos Rafael Nadal!.

  • Calling the vamosRafa_5 bean method for autonavigation. First, define the method in the managed bean to return a view (outcome) name (vamos is the view name for the vamos.xhtml file), as shown in the following code:

    public String vamosRafa_5(){
     return "vamos";
    }

Furthermore, extract the view name in the action attribute of any JSF UI component as shown in the following code:

<h:form>  
  <h:commandButton action="#{playersBean.vamosRafa_5()}" value="Vamos ..." />
</h:form>

Now, when the button labeled Vamos... is clicked, JSF will resolve the view name, vamos, to the vamos.xhtml file. Moreover, JSF will look for the vamos.xhtml file in the current directory and will navigate to it. Commonly, these navigation methods are used for conditional navigation between JSF pages.

Note

We have used parentheses to call a method, even when the method doesn't contain arguments. A special case is represented by the methods that contain an ActionEvent argument. These methods should be called without parentheses, except in the case when you override the ActionEvent argument altogether by passing and specifying custom argument(s).

EL expressions can also be used inside JavaScript function calls. For example, when you want to pass bean properties to a JavaScript function, you need to place them between quotes, as shown in the following code:

<h:form>
  <h:commandButton type="button" value="Click Me!" onclick="infoJS('#{playersBean.playerName}', '#{playersBean.playerSurname}')"/>
</h:form>

The JavaScript function for this is shown in the following code:

<script type="text/javascript">
  function infoJS(name, surname) {
    alert("Name: " + name + " Surname: " + surname);
  }
</script>
 

The conditional text in JSF


When you need to output the conditional text (without the HTML content), you can use the EL ternary operator, which has the following syntax:

boolean_test ? result_for_true : result_for_false

For example, you can use this operator to select between two CSS classes, as shown in the following code:

.red { color:#cc0000; } 
.blue { color: #0000cc; }

Now, you want to conditionally output a red or a blue text, as shown in the following code:

<h:outputText styleClass="#{playersBean.play == 'Left' ? 'red': 'blue'}" value="#{playersBean.play}"/>

So, if the value of play is Left, the text will be displayed using the red CSS class, and if it is not Left, then the blue class will be used.

Note

Keep in mind that the HTML content is not recommended (for security reasons do not use escape="false"), and the else part of the condition cannot be omitted.

For better understanding, let's look at another example. Remember that you have iterated over the titles_2013 array and output each item as shown in the following code:

<c:forEach var="title" items="#{playersBean.titles_2013}">
  <i>#{title}</i>,
</c:forEach>

Well, the output of this code will be something like the following screenshot:

Everything looks fine except the last comma, which should not appear since the US Open term is the last item to display. You can easily fix this issue with the EL ternary operator, as shown in the following code:

<c:forEach var="title" items="#{playersBean.titles_2013}" varStatus="v">
  <i>#{title}</i>
  #{v.last ? '':','}
</c:forEach>

Sometimes you just need to show or hide text based on a condition. For this, you can place a Boolean expression as the value of the rendered attribute (all JSF UI components have this attribute). For example, the following line of code will output a player's Facebook address only if such an address exists:

<h:outputText value="Facebook address: #{playersBean.facebook}" rendered="#{!empty playersBean.facebook}" />

Another common situation is to display or hide non-HTML text using two buttons of type "Show something..." and "Hide something...". For example, you can have a button labeled Show Career Prize Money and one labeled Hide Career Prize Money. Obviously, you want to display the career prize money when the first button is clicked and to hide the career prize money when the second button is clicked. For this, you can use the rendered attribute, as shown in the following code:

<h:form id="prizeFormId">
  <h:commandButton value="Show Career Prize Money">
  <f:ajax render="rnprizeid"  listener="#{playersBean.showPrizeMoney()}"/>
  </h:commandButton>
  <h:panelGrid id="rnprizeid">
    <h:outputText value="#{playersBean.prize}" rendered="#{playersBean.show_prize}">
      <f:convertNumber type="currency" currencySymbol="$" />
    </h:outputText>            
  </h:panelGrid>
  <h:commandButton value="Hide Career Prize Money">
    <f:ajax render="rnprizeid" 
            listener="#{playersBean.hidePrizeMoney()}"/>
  </h:commandButton>
</h:form>

Both the buttons use AJAX mechanism and an EL method expression to call the showPrizeMoney and hidePrizeMoney methods. These methods just modify the value of a boolean property, named show_prize, as shown in the following code:

private boolean show_prize = false;
...
public boolean isShow_prize() {
  return show_prize;
}
...
public void showPrizeMoney(){
  this.show_prize = true;
}
    
public void hidePrizeMoney(){
  this.show_prize = false;
}

When the request is complete, JSF will re-render the panel grid component with the ID rnprizeid; this was indicated in the render attribute of the f:ajax tag. As you can see, the re-rendered component is a panel that contains a simple h:outputText tag that outputs the prize property depending on the Boolean value of the EL expression present in the rendered attribute, as shown in the following code:

private int prize = 60941937;
...
public int getPrize() {
  return prize;
}

Showing and hiding text can be useful, but not enough. Usually, we need to show or hide the HTML content. For example, you may need to show or hide a picture:

<img src="resources/images/babolat.jpg" width="290" height="174"/>

This task can be easily accomplished by nesting the HTML code inside the Facelets ui:fragment tag, which supports the rendered attribute, as shown in the following code:

<ui:fragment rendered="#{playersBean.show_racquet}">
  <img src="#{resource['images:babolat.jpg']}" width="290" height="174"/>
</ui:fragment>

As you can see, the EL expression of the rendered attribute indicates a boolean property of the PlayersBean managed bean, as shown in the following code:

private boolean show_racquet = false;
...
public boolean isShow_racquet() {
  return show_racquet;
}

Now, you can let the user decide when to show or hide the image. You can easily adapt the preceding example, with two buttons labeled Show Image and Hide Image, or more elegant, you can use a checkbox, as shown in the following code:

...
<h:form>
  <h:selectBooleanCheckbox label="Show Image"valueChangeListener="#{playersBean.showHideRacquetPicture}">
    <f:ajax render="racquetId"/>
  </h:selectBooleanCheckbox>        
  <h:panelGroup id="racquetId">
    <ui:fragment rendered="#{playersBean.show_racquet}">
    <img src="resources/images/babolat.jpg" width="290" height="174"/>
  </ui:fragment>    
  </h:panelGroup>
</h:form>
...

The showHideRacquetPicture method sets the value of the show_racquet property to true or false, depending on the checkbox status. After this method is executed, JSF will re-render the content of the ui:fragment tag—this is accomplished via the HTML content rendered by the <h:panelGroup> tag, because the <ui:fragment> tag doesn't render the HTML content; therefore, it cannot be referenced by the ID. The following is the code for the showHideRacquetPicture method:

public void showHideRacquetPicture(ValueChangeEvent e){
  if(e.getNewValue() == Boolean.TRUE){
     this.show_racquet=true;
  } else {
     this.show_racquet=false;
  }
}

So, we can conclude that the rendered attribute can be used to conditionally output the HTML/non-HTML content. The user interaction and internal conditions can be used to play with this attribute value.

The complete application is named ch1_1.

 

Writing a custom EL resolver


EL flexibility can be tested by extending it with custom implicit variables, properties, and method calls. This is possible if we extend the VariableResolver or PropertyResolver class, or even better, the ELResolver class that give us flexibility to reuse the same implementation for different tasks. The following are three simple steps to add custom implicit variables:

  1. Create your own class that extends the ELResolver class.

  2. Implement the inherited abstract methods.

  3. Add the ELResolver class in faces-config.xml.

Next, you will see how to add a custom implicit variable by extending EL based on these steps. In this example, you want to retrieve a collection that contains the ATP singles rankings using EL directly in your JSF page. The variable name used to access the collection will be atp.

First, you need to create a class that extends the javax.el.ELResolver class. This is very simple. The code for the ATPVarResolver class is as follows:

public class ATPVarResolver extends ELResolver {

  private static final Logger logger = Logger.getLogger(ATPVarResolver.class.getName());
  private static final String PLAYERS = "atp";
  private final Class<?> CONTENT = List.class;
...
}

Second, you need to implement six abstract methods:

  • getValue: This method is defined in the following manner:

    public abstract Object getValue(ELContext context, Object base, Object property)

    This is the most important method of an ELResolver class. In the implementation of the getValue method, you will return the ATP items if the property requested is named atp. Therefore, the implementation will be as follows:

    @Override
    public Object getValue(ELContext ctx, Object base, Object property) {
    
    logger.log(Level.INFO, "Get Value property : {0}", property);
    
      if ((base == null) && property.equals(PLAYERS)) {
        logger.log(Level.INFO, "Found request {0}", base);    
        ctx.setPropertyResolved(true);          
        List<String> values = ATPSinglesRankings.getSinglesRankings();
        return values;
        }
      return null;
    }
  • getType: This method is defined in the following manner:

    public abstract Class<?> getType(ELContext context, Object base,Object property)

    This method identifies the most general acceptable type for our property. The scope of this method is to determine if a call of the setValue method is safe without causing a ClassCastException to be thrown. Since we return a collection, we can say that the general acceptable type is List. The implementation of the getType method is as follows:

    @Override
    public Class<?> getType(ELContext ctx, Object base, Object property) {
    
      if (base != null) {
        return null;
      }
    
      if (property == null) {
        String message = MessageUtils.getExceptionMessageString(MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "property");
        throw new PropertyNotFoundException(message);
      }
    
      if ((base == null) && property.equals(PLAYERS)) {
        ctx.setPropertyResolved(true);
        return CONTENT;
      }
      return null; 
    }
  • setValue: This method is defined in the following manner:

    public abstract void setValue(ELContext context, Object base, Object property, Object value)

    This method tries to set the value for a given property and base. For read-only variables, such as atp, you need to throw an exception of type PropertyNotWritableException. The implementation of the setValue method is as follows:

    @Override
    public void setValue(ELContext ctx, Object base, Object property, Object value) {
    
      if (base != null) {
        return;
      }
    
      ctx.setPropertyResolved(false);
      if (property == null) {
        String message = MessageUtils.getExceptionMessageString(MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "property");
        throw new PropertyNotFoundException(message);
      }
    
      if (PLAYERS.equals(property)) {
        throw new PropertyNotWritableException((String) property);
      }
    }
  • isReadOnly: This method is defined in the following manner:

    public abstract boolean isReadOnly(ELContext context, Object base, Object property)

    This method returns true if the variable is read-only and false otherwise. Since the atp variable is read-only, the implementation is obvious. This method is directly related to the setValue method, meaning that it signals whether it is safe or not to call the setValue method without getting PropertyNotWritableException as a response. The implementation of the isReadOnly method is as follows:

    @Override
    public boolean isReadOnly(ELContext ctx, Object base, Object property) {
      return true;
    }
  • getFeatureDescriptors: This method is defined in the following manner:

    public abstract Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base

    This method returns a set of information about the variables or properties that can be resolved (commonly it is used by a design time tool (for example, JDeveloper has such a tool) to allow code completion of expressions). In this case, you can return null. The implementation of the getFeatureDescriptors method is as follows:

    @Override
    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext ctx, Object base) {
      return null;
    }
  • getCommonPropertyType: This method is defined in the following manner:

    public abstract Class<?> getCommonPropertyType(ELContext context, Object base)

    This method returns the most general type that this resolver accepts. The implementation of the getCommonPropertyType method is as follows:

    @Override
    public Class<?> getCommonPropertyType(ELContext ctx, Object base) {
      if (base != null) {
        return null;
      }
      return String.class;
    }

Note

How do you know if the ELResolver class acts as a VariableResolver class (these two classes are deprecated in JSF 2.2) or as a PropertyResolver class? The answer lies in the first part of the expression (known as the base argument), which in our case is null (the base is before the first dot or the square bracket, while property is after this dot or the square bracket). When the base is null, the ELresolver class acts as a VariableResolver class; otherwise, it acts as a PropertyResolver class.

The getSinglesRankings method (that populates the collection) is called from the getValue method, and is defined in the following ATPSinglesRankings class:

public class ATPSinglesRankings {
    
  public static List<String> getSinglesRankings(){
       
    List<String> atp_ranking= new ArrayList<>();
        
    atp_ranking.add("1 Nadal, Rafael (ESP)");
    ...              
        
    return atp_ranking;
  }
}

Third, you register the custom ELResolver class in faces-config.xml using the <el-resolver> tag and specifying the fully qualified name of the corresponding class. In other words, you add the ELResolver class in the chain of responsibility, which represents the pattern used by JSF to deal with ELResolvers:

<application>
  <el-resolver>book.beans.ATPVarResolver</el-resolver>
</application>

Note

Each time an expression needs to be resolved, JSF will call the default expression language resolver implementation. Each value expression is evaluated behind the scenes by the getValue method. When the <el-resolver> tag is present, the custom resolver is added in the chain of responsibility. The EL implementation manages a chain of resolver instances for different types of expression elements. For each part of an expression, EL will traverse the chain until it finds a resolver capable to resolve that part. The resolver capable of dealing with that part will pass true to the setPropertyResolved method; this method acts as a flag at the ELContext level.

Furthermore, EL implementation checks, after each resolver call, the value of this flag via the getPropertyResolved method. When the flag is true, EL implementation will repeat the process for the next part of the expression.

Done! Next, you can simply output the collection items in a data table, as shown in the following code:

<h:dataTable id="atpTableId" value="#{atp}" var="t">
  <h:column>
    #{t}
  </h:column>
</h:dataTable>

Well, so far so good! Now, our custom EL resolver returns the plain list of ATP rankings. But, what can we do if we need the list items in the reverse order, or to have the items in uppercase, or to obtain a random list? The answer could consist in adapting the preceding EL resolver to this situation.

First, you need to modify the getValue method. At this moment, it returns List, but you need to obtain an instance of the ATPSinglesRankings class. Therefore, modify it as shown in the following code:

public Object getValue(ELContext ctx, Object base, Object property) {

  if ((base == null) && property.equals(PLAYERS)) {
    ctx.setPropertyResolved(true);
    return new ATPSinglesRankings();
  }
  return null;
}

Moreover, you need to redefine the CONTENT constant accordingly as shown in the following line of code:

private final Class<?> CONTENT = ATPSinglesRankings.class;

Next, the ATPSinglesRankings class can contain a method for each case, as shown in the following code:

public class ATPSinglesRankings {
    
  public List<String> getSinglesRankings(){
       
    List<String> atp_ranking= new ArrayList<>();
        
    atp_ranking.add("1 Nadal, Rafael (ESP)");
    ...             
        
    return atp_ranking;
  }
    
  public List<String> getSinglesRankingsReversed(){
       
    List<String> atp_ranking= new ArrayList<>();
                
    atp_ranking.add("5 Del Potro, Juan Martin (ARG)");
    atp_ranking.add("4 Murray, Andy (GBR)");        
    ...  
                
    return atp_ranking;
  }
    
  public List<String> getSinglesRankingsUpperCase(){
       
    List<String> atp_ranking= new ArrayList<>();
                
    atp_ranking.add("5 Del Potro, Juan Martin (ARG)".toUpperCase());                        
    atp_ranking.add("4 Murray, Andy (GBR)".toUpperCase());
    ...
                
    return atp_ranking;
  }
...
}

Since the EL resolver returns an instance of the ATPSinglesRankings class in the getValue method, you can easily call the getSinglesRankings, getSinglesRankingsReversed, and getSinglesRankingsUpperCase methods directly from your EL expressions, as shown in the following code:

<b>Ordered:</b><br/>
<h:dataTable id="atpTableId1" value="#{atp.singlesRankings}"var="t">
  <h:column>#{t}</h:column>
</h:dataTable>
<br/><br/><b>Reversed:</b><br/>
<h:dataTable id="atpTableId2" value="#{atp.singlesRankingsReversed}" var="t">
  <h:column>#{t}</h:column>
</h:dataTable>
<br/><br/><b>UpperCase:</b><br/>
<h:dataTable id="atpTableId3" value="#{atp.singlesRankingsUpperCase}" var="t">
  <h:column>#{t}</h:column>
</h:dataTable>

The complete applications to demonstrate custom ELResolvers are available in the code bundle of this chapter and are named ch1_2 and ch1_3.

In order to develop the last example of writing a custom resolver, let's imagine the following scenario: we want to access the ELContext object as an implicit object, by writing #{elContext} instead of #{facesContext.ELContext}. For this, we can use the knowledge accumulated from the previous two examples to write the following custom resolver:

public class ELContextResolver extends ELResolver {

  private static final String EL_CONTEXT_NAME = "elContext";

  @Override
  public Class<?> getCommonPropertyType(ELContext ctx,Object base){
    if (base != null) {
      return null;
    }
    return String.class;
  }

  @Override
  public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext ctx, Object base) {
    if (base != null) {
      return null;
  }
    ArrayList<FeatureDescriptor> list = new ArrayList<>(1);
    list.add(Util.getFeatureDescriptor("elContext", "elContext","elContext", false, false, true, 
    ELContext.class, Boolean.TRUE));
    return list.iterator();
  }

  @Override
  public Class<?> getType(ELContext ctx, Object base, Object property) {
    if (base != null) {
      return null;
    }
    if (property == null) {
      String message = MessageUtils.getExceptionMessageString(MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "property");
      throw new PropertyNotFoundException(message);
    }
    if ((base == null) && property.equals(EL_CONTEXT_NAME)) {
      ctx.setPropertyResolved(true);
    }
    return null;
  }

  @Override
  public Object getValue(ELContext ctx, Object base, Object property) {

    if ((base == null) && property.equals(EL_CONTEXT_NAME)) {
      ctx.setPropertyResolved(true);
      FacesContext facesContext = FacesContext.getCurrentInstance();
        return facesContext.getELContext();
    }
    return null;
  }

  @Override
  public boolean isReadOnly(ELContext ctx, Object base, Object property) {
    if (base != null) {
      return false;
    }
    if (property == null) {
      String message = MessageUtils.getExceptionMessageString(MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "property");
      throw new PropertyNotFoundException(message);
    }
    if (EL_CONTEXT_NAME.equals(property)) {
      ctx.setPropertyResolved(true);
      return true;
    }
    return false;
  }

  @Override
  public void setValue(ELContext ctx, Object base, Object property, Object value) {
    if (base != null) {
      return;
    }
    ctx.setPropertyResolved(false);
    if (property == null) {
      String message = MessageUtils.getExceptionMessageString(MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "property");
      throw new PropertyNotFoundException(message);
    }
    if (EL_CONTEXT_NAME.equals(property)) {
      throw new PropertyNotWritableException((String) property);
    }
  }
}

The complete application is named, ch1_6. The goal of these three examples was to get you familiar with the main steps of writing a custom resolver. In Chapter 3, JSF Scopes – Lifespan and Use in Managed Beans Communication, you will see how to write a custom resolver for a custom scope.

 

EL 3.0 overview


EL 3.0 (JSR 341, part of Java EE 7) represents a major boost of EL 2.2. The main features of EL 3.0 are as follows:

  • New operators +, =, and ;

  • Lambda expressions

  • Collection objects support

  • An API for standalone environments

In the upcoming sections, you will see how to use EL 3.0 features in JSF pages.

Working with the assignment operator

In an expression of type, x = y, the assignment operator (=), assign the value of y to x. In order to avoid an error of the kind PropertyNotWritableException, the x value must be an lvalue. The following examples show you how to use this operator in two simple expressions:

  • #{x = 3} evaluates to 3

  • #{y = x + 5} evaluates to 8

The assignment operator is right-associative (z = y = x is equivalent with z = (y = x)). For example, #{z = y = x + 4} evaluates to 7.

Working with the string concatenation operator

In an expression of type, x += y, the string concatenation operator (+=) returns the concatenated string of x and y. For example:

  • #{x += y} evaluates to 37

  • #{0 += 0 +=0 += 1 += 1 += 0 += 0 += 0} evaluates to 00011000

In EL 2.2, you can do this using the following code:

#{'0'.concat(0).concat(0).concat(1).concat(1).concat(0).concat(0).concat(0)}

Working with the semicolon operator

In an expression of type, x; y, x is first evaluated, and its value is discarded. Next, y is evaluated and its value is returned. For example, #‌{x = 5; y = 3; z = x + y} evaluates to 8.

Exploring lambda expressions

A lambda expression can be disassembled in three main parts: parameters, the lambda operator (->), and the function body.

Basically, in Java language, a lambda expression represents a method in an anonymous implementation of a functional interface. In EL, a lambda expression is reduced to an anonymous function that can be passed as an argument to a method.

It is important to not confuse Java 8 lambda expressions with EL lambda expressions, but in order to understand the next examples, it is important to know the fundamentals of Java 8 lambda expressions (http://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html). They don't have the same syntax, but they are similar enough to not cause notable discomfort when we need to switch between them.

An EL lambda expression is a parameterized ValueExpression object. The body of an EL lambda expression is an EL expression. EL supports several kinds of lambda expressions. The simplest type of EL lambda expressions are immediately invoked, for example:

  • #{(x->x+1)(3)} evaluates to 4

  • #{((x,y,z)->x-y*z)(1,7,3)} evaluates to -20

Further, we have assigned lambda expressions. These are invoked indirectly. For example, #‌{q = x->x+1; q(3)} evaluates to 4.

Indirectly, invocation can be used to write functions. For example, we can write a function to calculate n mod m (without using the % operator). The following example is evaluated to 3:

#‌{modulus = (n,m) -> m eq 0 ? 0 : (n lt m ? n: (modulus(n-m, m))); modulus(13,5)}

We can call this function from other expressions. For example, if we want to calculate the greatest common divisor of two numbers, we can exploit the preceding function; the following example is evaluated to 5:

#‌{gcd = (n,m) -> modulus(n,m) == 0 ? m: (gcd(m, modulus(n,m))); gcd(10, 15)}

Lambda expressions can be passed as arguments to methods. For example, in the following example, you call a method named firstLambdaAction—the lambda expression is invoked from this method:

#‌{lambdaBean.firstLambdaAction(modulus = (n,m) -> m eq 0 ? 0 : (n lt m ? n: (modulus(n-m, m))))}

Now, the firstLambdaAction method is as follows:

public Object firstLambdaAction(LambdaExpression lambdaExpression) {

  //useful in case of a custom ELContext
  FacesContext facesContext = FacesContext.getCurrentInstance();
  ELContext elContext = facesContext.getELContext();
  return lambdaExpression.invoke(elContext, 8, 3);

  //or simply, default ELContext:
  //return lambdaExpression.invoke(8, 3);
}

Another powerful feature of lambda expressions consists of nested lambda expressions. For example (first, is evaluated the inner expression to 7, afterwards the outer expression to as, 10 - 7): #‌{(x->x-((x,y)->(x+y))(4,3))(10)} evaluates to 3.

Do you think EL lambda expressions rocks? Well, get ready for more. The real power is unleashed only when we bring collection objects into equations.

Working with collection objects

EL 3.0 provides powerful support to manipulate collection objects by applying operations in a pipeline. The methods supporting the collection operations are implemented as ELResolvers, and lambda expressions are indicated as arguments for these methods.

The main idea behind manipulating collection objects is based on streams. More precisely, the specific operations are accomplished as method calls to the stream of elements obtained from the collection. Many operations return streams, which can be used in other operations that return streams, and so on. In such a case, we can say that we have a chain of streams or a pipeline. The entry in the pipeline is known as the source, and the exit from the pipeline is known as the terminal operation (this operation doesn't return a stream). Between the source and terminal operation, we may have zero or more intermediate operations (all of them return streams).

The pipeline execution begins when the terminal operation starts. Because intermediate operations are lazy evaluated, they don't preserve intermediate results of the operations (an exception is the sorted operation, which needs all the elements to sort tasks).

Now, let's see some examples. We begin by declaring a set, a list, and a map—EL contains syntaxes to construct sets, lists, and maps dynamically as follows:

#‌{nr_set = {1,2,3,4,5,6,7,8,9,10}}
#‌{nr_list = [1,2,3,4,5,6,7,8,9,10]}
#‌{nr_map = {"one":1,"two":2,"three":3,"four":4,"five":5,"six":6,"seven":7,"eight":8,"nine":9,"ten":10}}

Now, let's go a step further and sort the list in ascending/descending order. For this, we use the stream, sorted (this is like the ORDER BY statement of SQL), and toList methods (the latter returns a List that contains the elements of the source stream), as shown in the following code:

#‌{nr_list.stream().sorted((i,j)->i-j).toList()}
#‌{ nr_list.stream().sorted((i,j)->j-i).toList()}

Further, let's say that we have the following list in a managed bean named LambdaBean:

List<Integer> costBeforeVAT = Arrays.asList(34, 2200, 1350, 430, 57, 10000, 23, 15222, 1);

Next, we can apply 24 percent of VAT and compute the total for costs higher than 1,000 using the filter (this is like SQL's WHERE and GROUP BY statements), map (this is like SQL's SELECT statement), and reduce (this is like the aggregate functions) methods. These methods are used as follows:

#‌{(lambdaBean.costBeforeVAT.stream().filter((cost)-> cost gt 1000).map((cost) -> cost + .24*cost)).reduce((sum, cost) -> sum + cost).get()}

These were just a few examples of using collection objects in EL 3.0. A complete application named ch1_4 is available for download in the code bundle of this chapter. Since, in this application you can see more than 70 examples, I recommend you to take a look at it. Moreover, a nice example can be found on Michael Müller's blog at http://blog.mueller-bruehl.de/web-development/using-lambda-expressions-with-jsf-2-2/.

But, what if we want to take advantage of lambda expressions, but we don't like to write such expressions? Well, a solution can be to write parameterized functions based on lambda expressions, and call them in the JSTL style. For example, the following function is capable of extracting a sublist of a List:

#{get_sublist = (list, left, right)->list.stream().substream(left, right).toList()}

Now, we can call it as shown in the following code:

<ui:repeat value="#{get_sublist(myList, from, to)}" var="t">
  #{t}
</ui:repeat>

In the complete application, named ch1_5, you can see a bunch of 21 parameterized functions that can be used with Lists.

 

Summary


In this chapter, we saw that EL 2.2 expressions can be used to dynamically access data (read and write) stored in JavaBeans components, to call arbitrary static and public methods, and to perform arithmetic and logic operations. Finally, we saw that EL allows us to extend its capabilities with custom resolvers. Starting with EL 3.0, we can take advantage of new operators, lambda expressions, and support when working with collection objects.

While reading this book, you will see many examples of EL expressions in real cases. For example, in the next chapter, you will use EL expressions to explore JSF communication capabilities.

See you in the next chapter, where we will discuss JSF communications.

About the Author
  • Anghel Leonard

    Anghel Leonard is a Chief Technology Strategist and independent consultant with 20+ years of experience in the Java ecosystem. In daily work, he is focused on architecting and developing Java distributed applications that empower robust architectures, clean code, and high-performance. Also passionate about coaching, mentoring and technical leadership. He is the author of several books, videos and dozens of articles related to Java technologies.

    Browse publications by this author
Latest Reviews (4 reviews total)
I'm at the beginning, but it looks good. Provided examples can be more meaningful, but that is always a trade-off against simplicity.
A great book on the subject.
Mastering JavaServer Faces 2.2
Unlock this book and the full library FREE for 7 days
Start now