My JSF Books/Videos My JSF Tutorials OmniFaces/JSF PPTs
JSF 2.3 Tutorial
JSF Caching Tutorial
JSF Navigation Tutorial
JSF Scopes Tutorial
JSF Page Author Beginner's Guide
OmniFaces 2.3 Tutorial Examples
OmniFaces 2.2 Tutorial Examples
JSF Events Tutorial
OmniFaces Callbacks Usages
JSF State Tutorial
JSF and Design Patterns
JSF 2.3 New Features (2.3-m04)
Introduction to OmniFaces
25+ Reasons to use OmniFaces in JSF
OmniFaces Validators
OmniFaces Converters
JSF Design Patterns
Mastering OmniFaces
Reusable and less-verbose JSF code

My JSF Resources ...

Java EE Guardian
Member of JCG Program
Member MVB DZone
Blog curated on ZEEF
OmniFaces is an utility library for JSF, including PrimeFaces, RichFaces, ICEfaces ...

[OmniFaces Utilities] - Find the right JSF OmniFaces 2 utilities methods/functions

Search on blog

Petition by Java EE Guardians

Twitter

joi, 14 mai 2015

Working with OmniFaces Message Resolvers

I think that every JSF developer is familiar with JSF FacesMessage API. Nevertheless, as a quick reminder, a FacesMessage instance can be created, as below (in practice, we are using condensed and/or partial versions of the below code):

FacesMessage message = new FacesMessage();          
message.setDetail("detail");
message.setSummary("summary");
message.setSeverity(FacesMessage.SEVERITY_INFO/WARN/ERROR/FATAL);

// throw the message wrapped in a ValidatorException
throw new ValidatorException(message);
// throw the message wrapped in a ConverterException
throw new ConverterException(message);
// or add it to the current messages list as a global message
FacesContext.getCurrentInstance().addMessage(null, message);
// or add it to the current messages list as specific for a component via clientId
FacesContext.getCurrentInstance().addMessage(clientId, message);

Now, let's suppose that we have the following text that should "populate" a FacesMessage:

private final String LOGIN_MESSAGE = "Welcome, {0}! Your current rank on our website is {1}.";

As you can see, this text contains two parameters (placeholders) that should be provided before creating the FacesMessage instance. Of course, there are different kind of approaches for accomplish this task, but, in case you did not know, OmniFaces can take this text and transform it in a FacesMessage instance, and add the FacesMessage in the current list of messages, in just a single line of code. This line of code relies on the OmniFaces Messages utility class, which contains a collection of utility methods for the JSF API with respect to working with FacesMessage. In order to wrap this text into an info faces message, we can use Messages#createInfo(). The source code of this method is:

public static FacesMessage createInfo(String message, Object... params) {
 return create(FacesMessage.SEVERITY_INFO, message, params);
}

So, we need to pass the message (the text) and the parameters, and OmniFaces will do the rest for us:

FacesMessage message = Messages.createInfo(LOGIN_MESSAGE, "joe@gmail.com", "329");

Now, we can use this instance anyway we want, but, if we don't actually need the instance itself, and all we want is to add this message in the current list of messages via FacesContext#addMessage(), then we better use another OmniFaces utility named, Messages#addInfo(). This method uses internally the Messages#createInfo(). The source code is:

public static void addInfo(String clientId, String message, Object... params) {
 add(clientId, createInfo(message, params));
}

So, we can do this:

Messages.addInfo(null, LOGIN_MESSAGE, "joe@gmail.com", "329");

Note OmniFaces provides for each severity level similar utility methods.

But, what is actually happening behind the scene ? Well, if you check out the source code of the Messages#createInfo() method, you can see that it invokes another utility, named Messages#create(). The source code is listed below:

public static FacesMessage create(FacesMessage.Severity severity, String message, Object... params) {
 return new FacesMessage(severity, resolver.getMessage(message, params), null);
}

Well, notice the highlighted part! OmniFaces creates a standard FacesMessage, but it uses it own message resolver. Thanks to this resolver, OmniFaces provide support for parameterized messages! Practically, before creating the standard FacesMessage instance, OmniFaces uses the standard java.text.MessageFormat,  which is perfectly capable to create a "readable" information from a piece of text with placeholders and the values that must replace those placeholders. Further, this information is used to construct the standard FacesMessage.
So, at first glance , the message resolver seems to be  just an elegant why of " hiding" the MessageFormat invocation. Below, you can see the relevant code of the OmniFaces default message resolver:

// The message resolver allows the developers to change the way how messages are resolved via this interface
public interface Resolver {
 String getMessage(String message, Object... params);
}

// The default message resolver
private static final Resolver DEFAULT_RESOLVER = new Resolver() {

 @Override
 public String getMessage(String message, Object... params) {
  return MessageFormat.format(message, params);
 }
};

// Initialization of default message resolver
private static Resolver resolver = DEFAULT_RESOLVER;

// Allow developers to set a custom message resolver
public static void setResolver(Resolver resolver) {
 if (Messages.resolver == DEFAULT_RESOLVER) {
     Messages.resolver = resolver;
 } else {
     throw new IllegalStateException(ERROR_RESOLVER_ALREADY_SET);
 }
}

So, is obvious that the OmniFaces message resolver is more than just a simple wrapper of the MessageFormat, because it allows us to set a custom message resolver also. But, in order to provide a custom message resolver, we need to follow three steps:

·         Logically, first we need a message formatter that respects our needs.
·         Secondly, we need to wrap this message formatter in an OmniFaces Resolver, by implementing the Resolver interface and overriding the getMessage() method.
·         Thirdly, we need to respect the OmniFaces recommendation which says that "It's recommend to set another message resolver early during webapp's startup, for example with a ServletContextListener as WebListener, or a ServletContainerInitializer in custom JAR, or a ApplicationScoped bean, or an eagerly initialized Startup bean.".

Note A custom message resolver CAN BE SET ONLY ONCE.

So, let's suppose that we have the below text - basically, we are using some friendly placeholders, since {0} and {1} may not be very intuitive:

private String LOGIN_MESSAGE = "Welcome, {email}! Your current rank on our website is {rank}.";

This time the OmniFaces default message resolver cannot help us, so we need a custom one. This leads us to the first bullet above, which means that we need another message formatter. Of course, is not our aim to show you here how to write a message formatter, and, in this case, we don't even need to. We can simply use the MapFormat, which is capable to deal with messages as above.

Now, regarding the second bullet, we need to define the Resolver. This can be accomplish, like below:

public class MapFormatResolver implements Resolver {

 @Override
 public String getMessage(String string, Object... os) {
  HashMap params = (HashMap<String, String>) os[0];
  return MapFormat.format(string, params);
 }
}

Finally, regarding the third bullet, we choose to use an ApplicationScoped bean annotated with @Eager. More details about this annotation is available in OmniFaces Showcase, but, in a nutshell, we can have a bean that will be instantiated during application's startup, like this (we also added the necessary code for setting the MapFormatResolver):

@Eager
@ApplicationScoped
public class SetMessagesResolver {

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

 @PostConstruct
 public void init() {
  LOG.info("SETTING THE MAP FORMAT RESOLVER ...");
  Messages.setResolver(new MapFormatResolver());       
 }
}

Done! We have installed our custom message resolver, so we can try to use it:

private final String LOGIN_MESSAGE = "Welcome, {email}! Your current rank on our website is {rank}.";
...
Map params = new HashMap();
params.put("email", "joe@gmail.com");
params.put("rank", "329");
Messages.addInfo(null, LOGIN_MESSAGE, params);

Now, you know how to write and set an OmniFaces custom message resolver. Check out the complete application on GitHub.

Niciun comentariu :

Trimiteți un comentariu

JSF BOOKS COLLECTION

Postări populare

OmniFaces/JSF Fans

Follow by Email

Visitors Starting 4 September 2015

Locations of Site Visitors