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.
Join the DZone community and get the full member experience.
Join For FreeIntroduction
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
java:global/app-name/module-name/bean-name[! fully-qualified-interface-name]
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):
Specify a new attribute called "managed" on the corresponding annotations;
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
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:
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 initParamMap = FacesContext.getCurrentInstance().getExternalContext().getInitParameterMap();
Map<String, String> initParamMap = FacesContext.getCurrentInstance().getExternalContext().getInitParameterMap();
Generics for Converter and Validator Interfaces
Map<String, String> initParamMap = FacesContext.getCurrentInstance().getExternalContext().getInitParameterMap();
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>
Done! The complete application was tested under Payara server and it is available here.
Must Read
WebSocket integration (by Arjan Tijms)
Opinions expressed by DZone contributors are their own.
Comments