Over a million developers have joined DZone.

JavaServer Faces 2.3 Quick Reference

JSR 372 is currently in the Early Draft Review stage. The JSF 2.3 spec is not complete and will likely change significantly before finalization. In the meantime, here's a pragmatic deep-dive into JSF 2.3 in its current state.

· Java Zone

Check out this 8-step guide to see how you can increase your productivity by skipping slow application redeploys and by implementing application profiling, as you code! Brought to you in partnership with ZeroTurnaround.

Introduction

JSR 372 is currently in the Early Draft Review stage. The JSF 2.3 spec is not complete and will likely change significantly before finalization. In the meantime, here's a pragmatic deep-dive into JSF 2.3 in its current state.

For more resources, see JavaServer Faces 2.3 Tutorial and JSF 2.3 Repository Examples.

CDI Alignment

Injection and EL Resolving of JSF artifacts

As you probably know, JSF uses static entry methods and chaining to let the user obtain the various artifacts that it provides, such as the FacesContext, session map, external context, etc. However, this is pretty verbose and sometimes hard to intuit and understand. JSF 2.3 will therefore provide default producers for the most important artifacts, which at the moment are:

FacesContext
#{facesContext}

// 2.2
FacesContext facesContext = FacesContext.getCurrentInstance();

// 2.3
@Inject
private FacesContext facesContext;

ExternalContext
#{externalContext}

// 2.2
ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();

// 2.3
@Inject
private ExternalContext externalContext;

UIViewRoot
#{view}

// 2.2
UIViewRoot viewRoot = FacesContext.getCurrentInstance().getViewRoot();

// 2.3
@Inject
private UIViewRoot viewRoot;

ServletContext
#{application}

// 2.2
ServletContext servletContext = (ServletContext) FacesContext.getCurrentInstance().getExternalContext().getContext();

// 2.3
@Inject
private ServletContext servletContext;

Flash
#{flash}

// 2.2
Flash flash = FacesContext.getCurrentInstance().getExternalContext().getFlash();

// 2.3  
@Inject
private Flash flash;

Application Map
#{applicationScope}

// 2.2  
Map<String, Object> applicationMap = FacesContext.getCurrentInstance(). getExternalContext().getApplicationMap();

// 2.3  
@Inject
@ApplicationMap 
private Map<String, Object> applicationMap;

Session Map
#{sessionScope}

// 2.2
Map<String, Object> sessionMap = FacesContext.getCurrentInstance(). getExternalContext().getSessionMap();

// 2.3
@Inject
@SessionMap
private Map<String, Object> sessionMap;

View Map
#{viewScope}

// 2.2
Map<String, Object> viewMap = FacesContext.getCurrentInstance().getViewRoot().getViewMap();

// 2.3
@Inject
@ViewMap
private Map<String, Object> viewMap;

Request Map
#{requestScope}

// 2.2
Map<String, Object> requestMap = FacesContext.getCurrentInstance(). getExternalContext().getRequestMap();

// 2.3  
@Inject
@RequestMap
private Map<String, Object> requestMap;

Flow Map
#{flowScope}

// 2.2
Map<Object, Object> flowMap = FacesContext.getCurrentInstance().getApplication().getFlowHandler().getCurrentFlowScope();

// 2.3
@Inject
@FlowMap
private Map<Object, Object> flowMap;

Header Map
#{header}

// 2.2
Map<String, String> headerMap = FacesContext.getCurrentInstance().getExternalContext().getRequestHeaderMap();

// 2.3
@Inject
@HeaderMap
private Map<String, String> headerMap;

Cookie Map
#{cookie}

// 2.2
Map<String, Object> cookieMap = FacesContext.getCurrentInstance().getExternalContext().getRequestCookieMap();

// 2.3
@Inject
@RequestCookieMap
private Map<String, Object> cookieMap;

Init Param Map
#{initParam}

// 2.2
Map<String, String> initMap = FacesContext.getCurrentInstance(). getExternalContext().getInitParameterMap();

// 2.3
@Inject
@InitParameterMap
private Map<String, String> initMap;

Request Param Map
#{param}

// 2.2
Map<String, String> requestMap = FacesContext.getCurrentInstance(). getExternalContext().getRequestParameterMap();

// 2.3
@Inject
@RequestParameterMap
private Map<String, String> requestMap;

Request Values Map
#{paramValues}

// 2.2
Map<String, String[]> requestVMap = FacesContext.getCurrentInstance().getExternalContext().getRequestParameterValuesMap();

// 2.3
@Inject
@RequestParameterValuesMap
private Map<String, String[]> requestVMap;

Header Values Map
#{headerValues}

// 2.2
Map<String, String[]> headerVMap = FacesContext.getCurrentInstance().getExternalContext().getRequestHeaderValuesMap();

// 2.3
@Inject
@HeaderValuesMap
private Map<String, String[]> headerVMap;

Resource Handler
#{"resource"}

// 2.2
ResourceHandler resourceHandler = FacesContext.getCurrentInstance(). getApplication().getResourceHandler();

// 2.3
@Inject
private ResourceHandler resourceHandler;

The following artifacts are not injectable: Composite Component (#{cc}), Component (#{component}), Request (#{request}), Session (#{session})

Must Read

Injection and EL resolving of JSF artifacts (by Arjan Tijms)

Related

JSF 2.3 Injection and EL resolving of JSF artifacts

Injection in More JSF Artifacts

JSF 2.0 provides very modest support for injection in JSF artifacts. In JSF 2.1, very few JSF artifacts were injection targets. Starting with JSF 2.2, injection is possible in many more artifacts (check Mastering JavaServer Faces 2.2), but as the specification says, converters, validators, and behaviors are still not injection targets. It seems that this will be available from JSF 2.3. Until JSF 2.3, there were a few tricks to obtain validators/converters eligible for injection:

Until JSF 2.3 - Custom Validator Eligible For @Inject

You probably remember the days when you did this to obtain validators/converters eligible for injection:

@Named(value="fooValidator")
@RequestScoped
public class FooValidator implements Validator {
    @Inject
    // artifact

    @Override
    public void validate(FacesContext context, UIComponent component, Object value)
        ...
        // use the injected artifact
        ...
    }
}

// Usage example:
<h:inputText value="#{bean_property}" validator="#{fooValidator.validate}" />

Until JSF 2.3 - Custom Converter Eligible For @Inject

@Named(value="fooConverter")
@RequestScoped
public class FooConverter implements Converter{
    @Inject
    // artifact

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value) {
        ...
        // use the injected artifact
        ...
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value) {
        ...
        // use the injected artifact
        ...
    }
}

// Usage example:
<h:inputText value="#{bean_property}" converter="#{fooConverter}"/>

A more complicated task is using @EJB for injecting Enterprise JavaBeans (EJB) session beans. In this case, we need to manually lookup the EJB session bean from Java Naming and Directory Interface (JNDI). When the EJBs are deployed in a Web application ARchive (WAR), the lookup is generally of the following type:

java:app/app-name/bean-name[! fully-qualified-interface-name]

When the EJBs are in an Enterprise ARchive (EAR), the common lookup type is as follows:

java:global/app-name/module-name/bean-name[! fully-qualified-interface-name]
Inject EJB Bean From WAR

private FooEJBBean fooEJBBean; 
...
try {
    fooEJBBean = (FooEJBBean) new InitialContext().lookup("java:app/app-name/FooEJBBean"); 
} catch (NamingException e) {
    throw new ExceptionInInitializerError(e);
}

Inject EJB Bean From EAR

private FooEJBBean fooEJBBean;
...
try {
    fooEJBBean = (FooEJBBean) new InitialContext(). lookup("java:global/app-name/module-name/FooEJBBean");
} catch (NamingException e) {
    throw new ExceptionInInitializerError(e);
}

Starting with JSF 2.3 this gap has been filled and we can inject (using @Inject) in @FacesConverter (javax.faces.convert.Converter), @FacesValidator (javax.faces.validator.Validator), and @FacesBehavior (javax.faces.component.behavior.Behavior):

  1. Specify a new attribute called "managed" on the corresponding annotations;

  2. Add into WEB-INF the faces-config.xml with JSF 2.3 XSD (needed for JSF 2.3 milestones);

Converter (example)

@FacesConverter(value = "fooConverter", managed = true)
public class FooConverter implements Converter {
    @Inject
    // artifact

    @Override
    public Object getAsObject(FacesContext context, UIComponent component, String value) {
        ...
        // use the injected artifact
        ...
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, Object value) {
        ...
        // use the injected artifact
        ...
    }
}

Validator (example)

@FacesValidator(value = "fooValidator", managed = true)
public class FooValidator implements Validator { 
    @Inject
    // artifact

    @Override
    public void validate(FacesContext fc, UIComponent uic, Object o) throws ValidatorException {
        ...
        // use the injected artifact
        ... 
    }
}

Behavior (example)

@FacesBehavior(value = "fooBehavior", managed = true)
public class FooBehavior extends ClientBehaviorBase {
    @Inject
    // artifact

    @Override
    public String getScript(ClientBehaviorContext behaviorContext) {
        ...
        // use the injected artifact
        ...
    }
}

In milestones, you need to manually add the JSF 2.3 XSD, as below:

<?xml version="1.0" encoding="UTF-8"?>
<faces-config
    xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-facesconfig_2_3.xsd"
    version="2.3">
</faces-config>

Must Read

Injection in more JSF artifacts (by Arjan Tijms)

Related

JSF 2.3 Converters, validators, and behaviors as injection targets

JSF managed beans annotations are deprecated

Starting with JSF 2.3 the JSF "native" managed bean annotations are officially deprecated. So, there is no doubt now that CDI is the road to follow :)

Must Read

Native managed beans annotations deprecated (by Arjan Tijms)

JSF comes with @ManagedProperty compatible with CDI

The JSF managed bean annotations will be deprecated in JSF 2.3. This includes @ManagedProperty as well. But, compared to other annotations, this one has got an implementation that can be used with CDI managed beans. So, in CDI managed beans we can use the new, javax.faces.annotation.ManagedProperty. Let's see an example of injecting an instance of SourceBean into TargetBean, and a property of SourceBean into TargetBean:

CDI @ManagedProperty (example)

@Named
@RequestScoped
public class SourceBean {

 private String source;

 @PostConstruct
 public void init(){
  source = "SourceBean";
 }

 public String getSource() {
  return source;
 }

 public void setSource(String source) {
  this.source = source;
 }       
}

import javax.faces.annotation.ManagedProperty;
...
@Named
@RequestScoped
public class TargetBean {

 @Inject @ManagedProperty("#{sourceBean}")
 private SourceBean sourceBean;

 @Inject @ManagedProperty("#{sourceBean.source}")
 private String source;

 public void targetAction(){
  System.out.println("Injected bean: " + sourceBean);
  System.out.println("Injected property (via injected bean): " + sourceBean.getSource());
  System.out.println("Injected property: " + source);
 }
}

Must Read

CDI compatible @ManagedProperty (by Arjan Tijms)

AJAX

Generating a JavaScript function in the global JavaScript scope which allows the end user to execute a JSF AJAX request by just a function call in the JavaScript context

Sometimes, it may be useful to encapsulate the AJAX requests in JavaScript functions placed in the JavaScript global scope in such way that we can simply call them by name, without arguments, or with arguments that represent the data that should be passed to the server‐side via the encapsulated AJAX request. Obviously the jsf.ajax.request() and mojarra.ab() functions donʹt allow us to accomplish that, but as you will see, JSF 2.3 (and OmniFaces) will call the jsf.ajax.request() from a JavaScript global scoped function that can be easily called by its name.

Letʹs take a look at this simple HTML code:

<h5>Your Feedback About JSF 2.3: </h5>
<div>
 Your Name: <input id="userId" type="text"/>
 <p id="feedbackId" contenteditable="true">type your feedback here</p>
</div>
<br/>
<button onclick="sendFeedback();">Send Feedback</button>

Note that the sendFeedback() function should be defined as a global scoped JavaScript function capable to fire an JSF AJAX request. The user name and feedback should be added as parameters of the AJAX request.

JSF 2.3 comes with a handy new component named CommandScript (starting with JSF 2.3.0-m06) which is capable to solve this kind of tasks. This component be used by the page authors via the <h:commandScript/> tag. This component extends the javax.faces.component.UICommand (<h:commandXxx/>) which means that it inherits the major features of JSF commands, like action, actionListener, immediate, etc. Moreover, this component also supports nesting of <f:param/>, <f:actionListener/> and <f:setPropertyActionListener/>, exactly as in <h:commandXxx/>.

In addition, the foundation of CommandScript consists of the fact that it can generate a JavaScript function in the global JavaScript scope. Via this function call, the end‐user can execute JSF AJAX requests. There are two requirements for this component:

- The name attribute is mandatory, and it indicates the name of the generated JavaScript function (you can define namespaced function name also by using a dot, ".", inside the function name).

- The <h:commandScript/> must be nested in a <h:form/>.

For example, the simplest way to use <h:commandScript/>, is listed below:

<h:form>
 <h:commandScript name="sendFeedback"/>
</h:form>

This example is correct, but is doesnʹt do much! Basically, it generates in page a snippet of code as below:

<span id="j_idt5:j_idt6">
 <script type="text/javascript">
  var sendFeedback=function(o){
   var o=(typeof o==='object')&&o?o:{};
   mojarra.ab('j_idt5:j_idt6',null,'action',0,0,{'params':o})
  }
 </script>
</span>

It is important to notice that this is just a simple JSF AJAX request, so when we call the sendFeedback() function, we will fire an ʺemptyʺ JSF AJAX request. Obviously, we are interested to control the values of the execute and render attributes. This is very simple, because <h:commandScript/> supports the execute and render attributes exactly as <f:ajax/>, so we can do this:

<h:form>
 <h:commandScript name="sendFeedback" execute="@form" render=":savedId"/>
</h:form>

Now, the generated code becomes:

<span id="j_idt5:j_idt6">
 <script type="text/javascript">
  var sendFeedback=function(o){
   var o=(typeof o==='object')&&o?o:{};
   mojarra.ab('j_idt5:j_idt6',null,'action','@form','savedId',{'params':o})
  }
 </script>
</span>

With just a snap of a finger we can add the action:

<h:form>
<h:commandScript name="sendFeedback" execute="@form" 
                 render=":savedId" action="#{feedbackBean.send()}"/>
</h:form>

So far, so good! If you donʹt have extra information to pass to the server‐side, or it is collected from the form (e.g. <h:inputText/>), then this should be enough. But, in our case we have extra information to pass and this information comes through plain HTML code, so we have to find a solution for adding this information into the JSF AJAX request. Thanks to the <h:commandScript/> the generated function also supports a JavaScript object as an argument which will then end up in the HTTP request parameter map (donʹt forget about <f:param/>). In order to pass an object to the generated function, we need to wrap its call into a global JavaScript function, as below:

<h:outputScript>
 function sendFeedbackWrapper(){
  sendFeedback({user: document.getElementById("userId").value,
  feedback: document.getElementById("feedbackId").innerHTML });
 }
</h:outputScript>

And modify the button as:

<button onclick="sendFeedbackWrapper();">Send Feedback</button>

On the server‐side, the passed information can be easily extracted via the OmniFaces Faces#getRequestParameter() utility method:

String user = Faces.getRequestParameter("user"); // user
String feedback = Faces.getRequestParameter("feedback"); // feedback

Or, we can use the JSF pure solution:

Map<String, String> params = FacesContext.getCurrentInstance().
                                  getExternalContext().getRequestParameterMap();

String user = params.get("user"); // user 
String feedback = params.get("feedback"); // feedback

The complete application is available here.

Must Read

Ajax method invocation (by Arjan Tijms)

Lifecycle

System Event Published After View Rendered

As you probably know, JSF 2.2 comes with a significant number of events, but none of them are published right after the view is rendered. In other words, the PreRenderViewEvent event doesn't have a "friend" like PostRenderViewEvent. Well, starting with JSF 2.3 this is not true anymore, because PostRenderViewEvent is available and comes to add consistency to JSF events suite.

Subscribe via <f:event> From Page to Listen for PostRenderViewEvent (example)

<h:body>
    <f:event type="preRenderView" listener="#{fooBean.preRenderFooAction()}" />
    <f:event type="postRenderView" listener="#{fooBean.postRenderFooAction()}" />
</h:body>

Subscribe Programatically to Listen for PostRenderViewEvent From a Custom Component (example)

@FacesComponent(value = FooComponent.COMPONENT_TYPE, createTag = true)
public class FooComponent extends UIComponentBase implements SystemEventListener { 

    private static final Logger LOG = Logger.getLogger(FooComponent.class.getName()); 
    public static final String COMPONENT_FAMILY / COMPONENT_TYPE ...;

    public FooComponent () { 
    FacesContext.getCurrentInstance().getViewRoot().subscribeToViewEvent(PreRenderViewEvent.class, this);
        FacesContext.getCurrentInstance().getViewRoot().subscribeToViewEvent(PostRenderViewEvent.class, this);
    }

    @Override
    public void processEvent(SystemEvent event) throws AbortProcessingException {
        LOG.log(Level.INFO, "EVENT EMITTED: {0}", event);
    }

    @Override
    public void encodeBegin(FacesContext context) throws IOException {
        LOG.log(Level.INFO, "TomComponent#encodeBegin()");
    }

    @Override
    public void encodeEnd(FacesContext context) throws IOException {
        LOG.log(Level.INFO, "TomComponent#encodeEnd()");
    }

    @Override
    public boolean isListenerForSource(Object source) {
        return true;
    }
    ... 
}

And, the output will be:

EVENT EMITTED: javax.faces.event.PreRenderViewEvent[source=javax.faces.component.UIViewRoot] 
FooComponent#encodeBegin()
FooComponent#encodeEnd()
EVENT EMITTED: javax.faces.event.PostRenderViewEvent[source=javax.faces.component.UIViewRoot]

Obviously, PostRenderViewEvent hits processEvent() after encodeEnd(), which means that the rendering process is over.

Must Read

System event published after view rendered (by Arjan Tijms)

Related

Just tested JSF 2.3 PostRenderViewEvent under Payara 4.1

Java API

Support for the Iterable/Map Interface in UIData and UIRepeat

Iterating in UIData (e.g. <h:dataTable>)

<h:dataTable value="?" var="t">
   ...
</h:dataTable>

JSF 2.2

  • supports the List, native array, and JSF specific DataModel as input for its value binding;
  • you can directly use the Iterable (e.g. Set, Queue);
  • for Map you can use the OmniFaces function, of:mapToList();
  • since Iterable is directly supported, you can use it as #{fooBean.fooMap.entrySet()} or #{fooBean.fooMap.keySet()} or#{fooBean.fooMap.values()};

JSF 2.3 (example)

  • supports the List, native array, and JSF specific DataModel as input for its value binding;
  • you can directly use the Iterable (e.g. Set, Queue);
  • you can use Map directly:
<h:dataTable value="#{fooBean.fooMap}" var="t">
    #{t.key} #{t.value}
</h:dataTable>

Iterating in UIRepeat (e.g. <ui:repeat>)

<ui:repeat value="?" var="t">
    ...
</ui:repeat>

JSF 2.2

  • supports the List, native array, and JSF specific DataModel as input for its value binding;
  • for Iterable (e.g. Queue, Set) you can use the OmniFaces function, of:iterableToList();
  • for Set only (especially useful to Hibernate/JPA users, who are usually using the Set collections for entity relationships) you can use the OmniFaces function, of:setToList();
  • if EL 2.2 is present, for Iterable, you can use toArray() (e.g. #{fooBean.fooIterable.toArray()});
  • for Map you can use the OmniFaces function, of:mapToList();
  • if EL 2.2 is present, for Map, you can use toArray() (e.g. #{fooBean.fooMap.entrySet().toArray()} or #{fooBean.fooMap.keySet().toArray()} or #{fooBean.fooMap.values().toArray()});

JSF 2.3

  • supports the List, native array and JSF specific DataModel as input for its value binding
  • you can use directly the Iterable (e.g. Set, Queue)
<ui:repeat value="#{fooBean.fooSet}" var="t">
    ...
</ui:repeat>
  • you can use Map directly

<ui:repeat value="#{fooBean.fooMap}" var="t">
    #{t.key} #{t.value}
</ui:repeat>

Must Read

Support for the Iterable interface in UIData and UIRepeat (by Arjan Tijms)

Support for the Map interface in UIData and UIRepeat (by Arjan Tijms)

Related

JSF 2.0-2.3 Progress of Iterable/Map support in UIData/UIRepeat (OmniFaces support via utility functions)

Support for Custom Types in UIData and UIRepeat

JSF 2.3 will provide support for custom types in UIData and UIRepeat. With @FacesDataModel custom DataModel wrappers can be registered. Check the figure below:

Image title

Usage involve two steps:

  • register your wrapper by annotating it with @FacesDataModel
  • designates the type this wrapper is able to handle via forClass attribute

Wrapper Model

@FacesDataModel(forClass = fooCollection.class)
public class FooCollectionModel<E> extends DataModel<E> {
    @Override
    public E getRowData() {
        // access FooCollection here
    }

    @Override
    public void setWrappedData(Object fooCollection) {
        // likely just store fooCollection
    }
    // Other methods omitted for brevity
}

Use Collection in Your Beans

@Named
public class FooBean {
    public FooCollection<Foo> getFoo() {
        // return fooCollection
    }
}

In Data Table - It Works out of the Box

<h:dataTable value="#{fooBean.foo}" var="t">
    <h:column>
        #{t} | #{t.property}
    </h:column>
</h:dataTable>

With @FacesDataModel custom DataModel wrappers can be registered, but those wrappers cannot (yet) override any of the build-in types.

Must Read

Support for custom types in UIData and UIRepeat (by Arjan Tijms)

JSF 2.3 new feature: registrable DataModels (by Arjan Tijms)

Related

Registrable DataModels Example

Components

Auto detect Converter based on 1st UISelectMany item

Starting with JSF 2.3, more exactly with m07, we can take advantage of using the auto detection of convertors based on 1st UISelectMany item.The story behind the issue solved in JSF 2.3 is easy to understand via two examples.

Example 1:

Let's suppose the following code:

<h:form>          
 <h:selectManyListbox value="#{playerBean.selectedRanks}" 
                      converter="javax.faces.Integer">               
  <f:selectItems value="#{playerBean.playersRanks}"/>
 </h:selectManyListbox>
 <h:commandButton value="Select" 
                  action="#{playerBean.selectedAction()}"/>
</h:form> 

Let's start by supposing that the above built-in converter is not specified. Basically, the playersRanks is a list of integers that "populates" our list, and the selectedRanks represents the user selections:

private ArrayList<Integer> selectedRanks;
private static final ArrayList<Integer> playersRanks;

static {
 playersRanks = new ArrayList<>();
 playersRanks.add(1);
 playersRanks.add(2);
 playersRanks.add(3);
}

public ArrayList<Integer> getPlayersRanks() {
 return playersRanks;
}

public ArrayList<Integer> getSelectedRanks() {
 return selectedRanks;
}

public void setSelectedRanks(ArrayList<Integer> selectedRanks) {
 this.selectedRanks = selectedRanks;
}

So, the user may select the ranks and submit them without issues/errors. Even if no error occurred, we can notice a "strange" behavior if we try to run the following snippet of code:

<ui:repeat value="#{playerBean.selectedRanks}" var="i">
 #{i}: #{i.getClass()}
</ui:repeat>

The output reveals that the selected ranks are strings, not integers as we expected to see:

1: class java.lang.String
3: class java.lang.String

The explanation relies on the fact that "the generic type information of List<Integer> is lost during runtime and therefore JSF/EL who sees only List is not able to identify that the generic type is Integer and assumes it to be default String (as that's the default type of the underlying HttpServletRequest#getParameter()call during apply request values phase) - BalusC".

There are two approaches:
· explicitly specify a Converter
· use Integer[] instead

In this case, we can use the built-in javax.faces.Integer built-in converter. Now, we can perform the same test and the output will be:

1: class java.lang.Integer
3: class java.lang.Integer

Example 2:

This use case continue the story from the above use case. Cosider this code:

<h:form>          
 <h:selectManyListbox value="#{playerBean.selectedPlayersList}" 
                      converter="playerConverter">               
  <f:selectItems value="#{playerBean.playersList}" 
                 var="t" 
                 itemLabel="#{t.label}" 
                 itemValue="#{t}"/>
 </h:selectManyListbox>
 <h:commandButton value="Select" 
                  action="#{playerBean.selectedAction()}"/>
</h:form>

Basically, this time we use data for which we don't have a built-in converter available. The custom converter used here is needed because this time the list is "populated" with several Player instances:

private ArrayList<Player> selectedPlayersList;
private static final ArrayList<Player> playersList;

static {
 playersList = new ArrayList<>();
 playersList.add(new Player("Rafa", "Rafael Nadal"));
 playersList.add(new Player("Roger F", "Roger Federer"));
 playersList.add(new Player("Nole", "Novak Djokovic"));
}

public ArrayList<Player> getPlayersList() {
 return playersList;
}

public ArrayList<Player> getSelectedPlayersList() {
 return selectedPlayersList;
}

public void setSelectedPlayersList(ArrayList<Player> selectedPlayersList) {
 this.selectedPlayersList = selectedPlayersList;
}

// Player class snippet of code 
public class Player implements Serializable {

 private String label;
 private String value;

 public Player(String label, String value) {
  this.label = label;
  this.value = value;
 }
 ...

Remember from the above use case that the generic type of List<> is lost during runtime. Since the selected items are treated as strings instead of Player instances, the below code will cause an error because #{i.label} cannot be evaluated:

<ui:repeat value="#{playerBean.selectedPlayersList}" var="i">
 #{i.label}: #{i.getClass()}
</ui:repeat>

Since there is no built-in converter for converting strings to Player instances, we need a custom converter as below:

@FacesConverter("playerConverter")
public class PlayerConverter implements Converter {

 @Override
 public Object getAsObject(FacesContext context, UIComponent component,String value) {
  String[] parts = value.split("/");
  return new Player(parts[0],parts[1]);
 }

 @Override
 public String getAsString(FacesContext context, UIComponent component,Object value) {
  return value.toString();
 }   
}

Now, everything works as expected!

JSF 2.3

Well, while example 1 is pretty simple to "fix", the second example is not so easy since it requires us to write a custom converter. But, JSF 2.3 comes with a new feature that is capable to detect Converter based on 1st UISelectMany item and save us for using a built-in converter or writing a custom one for this purpose. So, in JSF 2.3 the below two codes will work as expected:

1: there is no need to specify a built-in conveter

<h:form>          
 <h:selectManyListbox value="#{playerBean.selectedRanks}">               
  <f:selectItems value="#{playerBean.playersRanks}"/>
 </h:selectManyListbox>
 <h:commandButton value="Select" 
                  action="#{playerBean.selectedAction()}"/>
</h:form> 

<ui:repeat value="#{playerBean.selectedRanks}" 
           var="i">
 #{i}: #{i.getClass()}
</ui:repeat>

2: there is no need to write/specify a custom converter

<h:form>          
 <h:selectManyListbox value="#{playerBean.selectedPlayersList}">               
  <f:selectItems value="#{playerBean.playersList}" 
                 var="t" itemLabel="#{t.label}" 
                 itemValue="#{t}"/>
 </h:selectManyListbox>
 <h:commandButton value="Select" 
                  action="#{playerBean.selectedAction()}"/>
</h:form>

<ui:repeat value="#{playerBean.selectedPlayersList}" 
           var="i">
 #{i.label}: #{i.getClass()}
</ui:repeat>

For studying the implementation please download Mojarra source code and check out the javax.faces.component.UISelectMany, com.sun.faces.renderkit.html_basic.MenuRenderer (especially, MenuRenderer#getConverterForSelectManyValues()method) and com.sun.faces.util.getConverterForClass().

Must Read

Automatic conversion in UISelectMany for Collection (by Arjan Tijms)

Import constants/enums

Let's checkout a common practice for declaring constants in Java using the public static final declaration:

public class Circle {
 private static final float PI = 3.14f;
 private static final String UNIT = " radians";
 ...
}

Also, Java interfaces and enums are very useful in applications (they are part of the Java language ʺbricksʺ):

public interface Car {
 public String MODEL = "Logan";
 public String COUNTRY = "RO";
}

public enum Cars {
 CITROEN, LOGAN, BMW;
}

Now, letʹs suppose that we have these artifacts in a JSF application, as a JSF page author we need to use them in page via EL, as below:

Use constants:

#{Circle.PI}
#{Circle.UNIT}

Use interfaces and enums:

#{Car.MODEL}

There is no elegant way to say that the above usages will simply not work!

So, now let's consider the following example. First, we define an enum:

public enum PlayerEnum {

 FIRST, SECOND, THIRD;

 public Integer getRank() {
  switch (name()) {
          case "FIRST":
                return 1;
          case "SECOND":
                return 2;
          case "THIRD":
                return 3;
          default:
                return 0;
  }
 }
}

Further, we use this enum in a CDI managed bean:

@Named
@RequestScoped
public class PlayerBean implements Serializable {

 private static final long serialVersionUID = 1L;

 private PlayerEnum selectedPlayerEnum;
 public static final String NAME_C = "Rafael Nadal";

 public PlayerEnum getSelectedPlayerEnum() {
  return selectedPlayerEnum;
 }

 public void setSelectedPlayerEnum(PlayerEnum selectedPlayerEnum) {
  this.selectedPlayerEnum = selectedPlayerEnum;
 }
}

Well, OmniFaces comes with a tag handler named, <o:importConstants/> that is capable to map of all constant field values of the given fully qualified name of a type in the request scope (it works with enums also). This tag handler is detailed in the book "Mastering OmniFaces". PrimeFaces also comes with support for import constants and enums. Before PrimeFaces Elite 5.3.8 this support was available in PrimeFaces Extension as <pe:importConstants/> and <pe:importEnum/>. Afterwards, this support was moved from PrimeFaces extension to PrimeFaces Elite 5.3.8, and is available via <p:importConstants/> and <p:importEnum/>. This tags are detailed in book, "PrimeFaces & OmniFaces - Powers Combined".

Starting with JSF 2.3-m07, we can import constants and enums via the new <f:importConstants/> tag. Check the NAME_C constant from above. Well, this constant (and any other constant from PlayerBean) can be accessed via EL once we import them as below (the type attribute is required and its value represents the fully qualified name of the class/interface/enum to import the constant field values for):

<f:importConstants type="javaee8.jsf23.PlayerBean" />

The <f:importConstants/> supports the var attribute also. You can use it to indicate an alias (the name of the request attribute which exposes the mapping of the constants in the request scope), as
below:

<f:importConstants type="javaee8.jsf23.PlayerBean" var="CONSTANTS" />

Now, we can write this:

#{CONSTANTS.NAME_C}

The PlayerEnum can be imported exactly the same:

<f:importConstants type="javaee8.jsf23.PlayerEnum" />

This allows us to loop the enum, as below (returns FIRST SECOND THIRD):

<ui:repeat value="#{PlayerEnum.values()}" var="t">
 #{t}
</ui:repeat>

Now, we can nominate the enum constants:

#{PlayerEnum.FIRST} // returns FIRST
#{PlayerEnum.FIRST.rank} // returns 1

We can put all together and provide enum values as dropdown items:

<h:form>
 <h:selectOneMenu value="#{playerBean.selectedPlayerEnum}">
  <f:selectItems value="#{PlayerEnum}" />
  <f:ajax render="@form" />
 </h:selectOneMenu>
 #{playerBean.selectedPlayerEnum}
 [#{playerBean.selectedPlayerEnum == PlayerEnum.FIRST}]
</h:form>

We can glue all the examples above in the below view:

<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:f="http://xmlns.jcp.org/jsf/core">

 <f:metadata>
  <f:importConstants type="javaee8.jsf23.PlayerEnum" />
  <f:importConstants type="javaee8.jsf23.PlayerBean" />
  <f:importConstants type="javaee8.jsf23.PlayerBean" var="CONSTANTS" />
 </f:metadata>
 <h:head>
  <title>JSF 2.3 - ImportConstants</title>             
 </h:head>
 <h:body>                   
  <h5>Display the 'NAME_C' constant value</h5>
  <h:outputText value="#{PlayerBean.NAME_C}"/><br/>
  <h:outputText value="#{CONSTANTS.NAME_C}"/><br/>

  <h5>Loop enum values</h5>
  <ui:repeat value="#{PlayerEnum.values()}" var="t">
   #{t}
  </ui:repeat>

  <h5>Providing enum values as dropdown items and test again 'FIRST'</h5>
  <h:form>
   <h:selectOneMenu value="#{playerBean.selectedPlayerEnum}">        
    <f:selectItems value="#{PlayerEnum}" />
    <f:ajax render="@form" />
   </h:selectOneMenu>
   #{playerBean.selectedPlayerEnum}
   [#{playerBean.selectedPlayerEnum == PlayerEnum.FIRST}]
  </h:form>

  <h5>Providing enum values as dropdown items by var  and test again 'FIRST'</h5>
  <h:form>
   <h:selectOneMenu value="#{playerBean.selectedPlayerEnum}">        
    <f:selectItems value="#{PlayerEnum.values()}" var="t" itemLabel="#{t.rank}" itemValue="#{t}" />
    <f:ajax render="@form" />
   </h:selectOneMenu>
   #{playerBean.selectedPlayerEnum}
   [#{playerBean.selectedPlayerEnum == PlayerEnum.FIRST}]
  </h:form> 
 </h:body>
</html>

The complete example is available here.

Must Read

Importing constants into EL namespace (by Arjan Tijms)

Type-Safety

Generics for ExternalContext#getInitParameterMap

In JSF 2.2 the ExternalContext#getInitParameterMap() returns a raw Map. Starting with JSF 2.3, this method was extended to support generics types.

JSF 2.2 - Raw Map

Map initParamMap = FacesContext.getCurrentInstance().getExternalContext().getInitParameterMap();
JSF 2.3 - Map<String, String>

Map<String, String> initParamMap = FacesContext.getCurrentInstance().getExternalContext().getInitParameterMap();
Generics for Converter and Validator Interfaces

As you probably know, in JSF 2.2 we can write a custom converter by extending the Converter interface, and a custom validator by extending the Validator interface. The methods defined in these interfaces works with Object class. Starting with JSF 2.3, Converter and Validator have now been parameterized and implementations can concisely define the exact input type.

Mojarra 2.3.0-m04 Converter Snippet of Source Code

public interface Converter<T> {
    T getAsObject(FacesContext context, UIComponent component, String value);
    String getAsString(FacesContext context, UIComponent component, T value);
}

Mojarra 2.3.0-m04 Validator Snippet of Source Code

public interface Validator<T> {
    void validate(FacesContext context, UIComponent component, T value);
}

For example, let's suppose that we have the following simple class:

public class User implements Serializable {
    private String name;
    private String email;

    public User(String name, String email) {
        this.name = name;
        this.email = email;
    }
    ...
    // getters and setters
    // equal and hash code
    ...
}

We can convert/validate a string against this class by indicating the User type in our converter/validator:

JSF 2.3 Custom Converter With Defined Type (example)

@FacesConverter(value = "UserConverter")
public class UserConverter implements Converter<User> {
    @Override
    public User getAsObject(FacesContext context, UIComponent component, String value) {
        ...
    }

    @Override
    public String getAsString(FacesContext context, UIComponent component, User value) {
        // no need to check value type and cast it
        ...
    }
}

JSF 2.3 Custom Validator With Defined Type (example)

@FacesValidator(value = "UserValidator")
public class UserValidator implements Validator<User> {
    @Override
    public void validate(FacesContext fc, UIComponent uic, User o) throws ValidatorException {
        // no need to check value type and cast it
        ...
    }
}

In JSF 2.3, you can still write custom converters/validators as in JSF 2.2. If you don't want to take advantage of the new generic parameters, you can use the classical approach.

Must Read

Generics for Converter and Validator interfaces (by Arjan Tijms)

Related

JSF 2.3 Take advantage of the new generic parameters in Converter<T> and Validator<T>


Configuration

Facelets default non-hot reload in production

JSF has the ability to cache Facelets. In order to update the cache, JSF performs periodic checks of Facelets views changes. In the development stage, you may need to perform this check much more often than in production. For this, you can set the javax.faces.FACELETS_REFRESH_PERIOD context parameter as shown in the following example (the value represents the number of seconds between two consecutive checks).

5 Seconds Timeout

<context-param>
    <param-name>javax.faces.FACELETS_REFRESH_PERIOD</param-name>
    <param-value>5</param-value>
</context-param>

There are two special values:

  • -1 means no refresh (cache indefinitely)
  • 0 means no caching at all (always hot reload)

Starting with JSF 2.3, when the project stage is Production (default) the Facelets refresh period is -1 (no refresh).

Must Read

Facelets default to non-hot reload in production (by Arjan Tijms)

The f:viewParam BV/event fail has been fixed

The standard UIViewParameter implementation uses in JSF 2.0-2.2 an internal "is required" check when the submitted value is null, hereby completely bypassing the standard UIInput validation, including any bean validation annotations and even the PreValidateEvent and PostValidateEvent events. This is not desired.

The workaround was added in OmniFaces 2.0. In JSF 2.3, this has been fixed and has only effect when javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL context parameter is set to true.


Conversion/Validation

Class-level Bean Validation on CDI-Based Backing Beans

JSF 2.2 has several limitations in using Bean Validation. One of them involves the fact the JSF cannot validate the class or method level constraints (so called, cross‐field validation), only field constrains. JSF 2.3 will come with a new tag named, <f:validateWholeBean/>. As its name suggest, this tag is enables class level validation. This tag contains two important attributes:

  • value - A ValueExpression referencing the bean to be validated.
  • validationGroups - A comma-separated list of validation groups. A validation group is a fully-qualified class name. This feature causes a temporary copy of the bean referenced by the value attribute, for the sole purpose of populating the bean with field values already validated by <f:validateBean/> and then performing class-level validation on the copy. Regardless of the result of the class-level validation, the copy is discarded.

JSF 2.3 Class-Level Validation (example)

Here is a brief example to ensure that the provided name and e-mail fields (contacts) are individually valid and also the e-mail start with that name (e.g. valid: nick, nick_ulm@yahoo.com).

ContactValidator Class Implementation

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class ContactValidator implements ConstraintValidator<ValidContact, ContactBean> {
    @Override
    public void initialize(ValidContact constraintAnnotation) {
        // NOOP 
    }

    @Override
    public boolean isValid(ContactBean value, ConstraintValidatorContext context) {
        return value.getEmail().startsWith(value.getName());
    }
}

Note that a ContactBean instance is passed to the isValid() method. This method will only be called if the individual properties of the ContactBean are valid. This fact allows the isValid() method to inspect the properties and perform effective class-level validation.

ValidContact Class Implementation

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;

@Constraint(validatedBy = {ContactValidator.class})
@Documented
@Target(TYPE)
@Retention(RUNTIME)
public @interface ValidContact {
    String message() default "Invalid contacts !";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}

ContactBean Class Implementation

This is the backing Bean:

@Named
@RequestScoped
@ValidContact(groups = validateBean.ContactGroup.class)
public class ContactBean implements Serializable, Cloneable {
    private static final long serialVersionUID = 1L;

    @Size(min = 3, max = 20, message = "Please enter a valid name (between 3-20 characters)!",
    groups = validateBean.ContactGroup.class)
    private String name;

    @Pattern(regexp = "[a-zA-Z0-9]+@[a-zA-Z0-9]+\\.[a-zA-Z0-9]+",
    message = "Please enter a valid formated e-mail !",
    groups = validateBean.ContactGroup.class)
    private String email;

    // getters and setters

    @Override
    protected Object clone() throws CloneNotSupportedException {
        ContactBean other = (ContactBean) super.clone();
        other.setName(this.getName());
        other.setEmail(this.getEmail());
        return other;
    } 
}

ContactGroup Interface Implementation

package validateBean;

public interface ContactGroup {
    // NOPE
}

Facelets View

<h:body>
    <h:form>

        Name:
        <h:inputText value="#{contactBean.name}">
            <f:validateBean validationGroups="validateBean.ContactGroup" />
        </h:inputText>

        E-mail:
        <h:inputText value="#{contactBean.email}">
            <f:validateBean validationGroups="validateBean.ContactGroup" />
        </h:inputText>

        <h:commandButton value="Contact Admin" action="contact_admin"/>
        <f:validateWholeBean value="#{contactBean}" validationGroups="validateBean.ContactGroup" />
    </h:form>
</h:body>

The following feature must explicitly be enabled by setting the following application parameter (context parameter) in web.xml: javax.faces.validator.ENABLE_VALIDATE_WHOLE_BEAN. If this parameter is not set, or is set to false, this tag must be a no-op:

<context-param>
    <param-name>javax.faces.validator.ENABLE_VALIDATE_WHOLE_BEAN</param-name>
    <param-value>true</param-value>
</context-param>

Must Read

Class level bean validation (by Arjan Tijms)

Related

JSF 2.3 Class-level bean validation on CDI based backing beans

JSR 310 Alignment

JSF 2.3 will have Java 8 as a minimum dependency. One of the affected artifacts is the <f:convertDateTime/> built-in converter which has been updated in order to support new types for the type attribute, and, depending on the value of the type attribute, the converter will use the java.text.SimpleDateFormat or java.time.format.DateTimeFormatter class for formatting.

The upcoming JSF 2.3 now adds new possible values for the type attribute, as follows:

localDate, localTime, localDateTime, offsetTime, offsetDateTime, zonedDateTime

Simple Bean

@Named
@RequestScoped
public class MyBean {
    private LocalDate localDate;
    private LocalTime localTime;
    private LocalDateTime localDateTime;
    private OffsetTime offsetTime;
    private OffsetDateTime offsetDateTime;
    private ZonedDateTime zonedDateTime;

    public MyBean() {
        localDate = LocalDate.now();
        localTime = LocalTime.now();
        localDateTime = LocalDateTime.now();
        offsetTime = OffsetTime.now();
        offsetDateTime = OffsetDateTime.now();
        zonedDateTime = ZonedDateTime.now();
    }
    // getters and setters
}

Using offsetTime

<h:outputText value="#{myBean.offsetTime}">
    <f:convertDateTime type="offsetTime" pattern="H:mm:ss:SSS" />
</h:outputText>

Using offsetDateTime

<h:outputText value="#{myBean.offsetDateTime}">
    <f:convertDateTime type="offsetDateTime" pattern="EEE, MMM d, ''yy" />
</h:outputText>

Using zonedDateTime

<h:outputText value="#{myBean.zonedDateTime}">
    <f:convertDateTime type="zonedDateTime" pattern="yyyy.MM.dd G 'at' hh:mm:ss z" />
</h:outputText>

When the converter type attribute value is date, time or both, JSF (2.2 and 2.3) uses the java.text.SimpleDateFormat class. Starting with JSF 2.3, when the converter type attribute value is localDate, localTime, localDateTime, offsetTime, offsetDateTime or zonedDateTime, the java.time.format.DateTimeFormatter class will be used.

Must Read

JDK 8 time support in f:convertDateTime (by Arjan Tijms)

Related

JSF 2.3 align the <f:convertDateTime/> to the new data and time classes in JDK 8 (JSR 310)

Networking

Starting with JSF 2.3-m05 we can take advantage of a brand new feature - register a web socket push connection in client side. Thanks to the JSF team (especially to Bauke Scholtz (aka BalusC)) this feature is available in today milestone via <f:websocket/> tag. 

Register a web socket push connection in client side

Let's see an example of using the <f:websocket/>. In JSF page, we need to add the <f:websocket/> tag with its two required attributes:

  • channel - This is javax.el.ValueExpression that must be evaluated to String and it represents the name of the web socket channel. A channel name is restricted to alphanumeric characters, hyphens, underscores and periods. A channel can have multiple open web sockets, and each of these sockets will receive the same push notification from the server.

  • onmessage - This is javax.el.ValueExpression that must be evaluated to String and it represents the a JavaScript listener function that is automatically invoked when a push notification is received from the server.

The signature of the listener function for onmessage is of type:

function fooListener(message, channel, event) {                      
 // message - the message pushed by the server
 // channel - the channel name
 // event - the raw MessageEvent instance
}

So, a simple <f:websocket/> tag usage will look like this:

<f:websocket channel="clock" onmessage="socketListener" />

<div id="clockId"></div>

<script type="text/javascript">
 function socketListener(message, channel, event) {                      
  document.getElementById("clockId").innerHTML += message + "<br/>";
 }
</script>

By default, when we start the application, the web socket is automatically connected and open. As long as the document is open the web socket is open. When the document is unloaded the web socket is automatically closed. In the web socket is initially successfully connected but the connection is closed as a result of e.g. a network error or server restart, JSF will try to auto-reconnect it at increasing intervals.

Now, let's focus on the server side. Here we have to take into account the push messages mechanism. This mechanism is based on javax.faces.push.PushContextinterface and javax.faces.push.Push API.

First, you need to know that by default the web socket is application scoped. This means that the managed bean that can push messages to this web socket must be in application scope (annotated with @ApplicationScope). In this case, the push message can be sent by all users and the application itself.

Furthermore, you have to inject PushContext via @Push annotation on the given channel name in any CDI/container managed artifact. For example:

@Inject
@Push(channel = "clock")
private PushContext push;

Finally, we need to write an action method capable to push messages to web socket via PushContext. For example:

public void clockAction(){
 Calendar now = Calendar.getInstance();

 String time = now.get(Calendar.HOUR_OF_DAY) + ":" +
               now.get(Calendar.MINUTE) + ":" +
               now.get(Calendar.SECOND);
 LOG.log(Level.INFO, "Time: {0}", time);

 push.send(time);
}

Let's glue everything together. First, the JSF page:

<h:body>        
 <h:form>           
  <h:commandButton value="Clock" action="#{pushBean.clockAction()}">
   <f:ajax />
  </h:commandButton>
 </h:form>

 <f:websocket channel="clock" onmessage="socketListener" />       

 <hr/>
 <div id="clockId"></div>

 <script type="text/javascript">
  function socketListener(message, channel, event) {                      
   document.getElementById("clockId").innerHTML += message + "<br/>";
  }
 </script>   
</h:body>

Next, our simple CDI bean:

@Named
@ApplicationScoped
public class PushBean implements Serializable {

 private static final Logger LOG = Logger.getLogger(PushBean.class.getName());

 @Inject
 @Push(channel = "clock")
 private PushContext push;

 public void clockAction(){
  Calendar now = Calendar.getInstance();

  String time = now.get(Calendar.HOUR_OF_DAY) + ":" +
                now.get(Calendar.MINUTE) + ":" + now.get(Calendar.SECOND);
  LOG.log(Level.INFO, "Time: {0}", time);

  push.send(time);
 }
}

For those implementations that do not support adding an EndPoint dynamically (at the moment only GlassFish/Tyrus), a fake one has to be defined by the application. As BalusC pointed out, this fake  endpoint should look like below:

import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.Session;

public class FakeEndpoint extends Endpoint {

 @Override
 public void onOpen(Session session, EndpointConfig config) {
  // https://java.net/jira/browse/WEBSOCKET_SPEC-240
 }   
}

Finally, the m05 requires the following settings in web.xml:

<context-param>
 <param-name>javax.faces.ENABLE_CDI_RESOLVER_CHAIN</param-name>
 <param-value>true</param-value>
</context-param>   
<context-param>
 <param-name>javax.faces.ENABLE_WEBSOCKET_ENDPOINT</param-name>
 <param-value>true</param-value>
</context-param>

Image title

Done! The complete application was tested under Payara server and it is available here.

Must Read

WebSocket integration (by Arjan Tijms)

The Java Zone is brought to you in partnership with ZeroTurnaround. Check out this 8-step guide to see how you can increase your productivity by skipping slow application redeploys and by implementing application profiling, as you code!

Topics:
java ,jsf ,ui ,server-side

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}