Wednesday, March 11, 2015

JSF2 Coding and Design Standards

The following details coding and design standards I have chosen for a web project I am currently working on.

Environment Details

  • Java 1.7
  • WebSphere Application Server 8.5 (with IBM SDK 1.7)
    • JEE 6
    • Servlet 3.0
    • JSP 2.2
    • JSF 2.0 (MyFaces 2.0)
    • el-api 2.2
  • JBoss EAP 6.3.0
    • JEE 6
    • Servlet 3.0
    • JSP 2.2
    • JSF 2.1 (Mojarra 2.1.28)
    • el-api 2.2, JSTL 1.2
  • PrimeFaces 5.1
  • SpringFramework 4.1.1
  • Relational database (Oracle 12c / Informix)
The web application is broken up into the following layers :

  • Service layer
    • Business logic layer
    • Data access layer
  • Presentation layer
    • JSF managed beans
    • XHTML pages

Business logic layer

The business logic layer responsibility is to:
  • Contain all business specific logic
  • Define transactional boundaries using Spring transaction management
  • Handle exceptions

Data access layer

Normally I would not have had a data access layer and just have the business logic layer handle all the DB transactions. However for this project we needed to support multiple databases so having the DB interactions in a separate layer enables us to choose which DB classes to use for a particular database. We made a decision to use Spring JDBC and not have any ORM library. The data access layer should contain methods that are only used query or to update the database, there should be no business logic code in them.

JSF Managed Beans

The JSF managed beans are Java objects managed by JSF and along with the Facelets XHTML pages they form part of the presentation layer of the web application.

Managed Bean Scope

Depending on your need a managed bean can have one of the following scopes:
  • Application Scoped
  • Session Scoped
  • View Scoped
  • Request Scoped
  • Custom Scoped
  • None 

Spring integration

You would need to add the following to your web.xml file:
    
        contextConfigLocation
        /WEB-INF/web-ctx.xml
    

    
        org.springframework.web.context.ContextLoaderListener
    
    
        org.springframework.web.context.request.RequestContextListener
    
 
And the Spring EL resolver to your faces-config.xml:
  
    org.springframework.web.jsf.el.SpringBeanFacesELResolver
  
In order to add Spring autowire capabilities to a JSF managed bean we made use of Springs WebApplicationContextUtils class:
 @PostConstruct
 private void init() {
   ExternalContext externalContext = FacesContext.getCurrentInstance().getExternalContext();
   ServletContext servletContext = (ServletContext) externalContext.getContext();
   WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext).getAutowireCapableBeanFactory().autowireBean(this);
 }

A view scoped bean lives for as long as you interact with the same view. As soon as you navigate away to another view then the view scoped bean will not be available in the next request. View scoped beans need to implement the Serializable interface.

In order to have Spring autowiring capabilities available in view scoped beans you should make the autowired Spring managed beans transient so that they are not serialised and on deserialization you will need to autowire the Spring beans in the same way we did for the PostConstruct init method:
  
  private Object readResolve() {
   init();
   return this;
 }
Another issue with view scoped managed beans is that you won't be able to inject a resource bundle into a view scoped managed bean as the ResourceBundle class is not serializable. To get around this you can do one of the following:

Use a different scope for your bean and not view scoped
Make your resource bundle transient so that it doesn't get serialized and then manually reinitialise it on deserialisation:
 
 @ManagedProperty("#{msg}")
 private transient ResourceBundle msgBundle;

 private Object readResolve() {
   msgBundle = Faces.getApplication().evaluateExpressionGet(Faces.getContext(), "#{msg}", ResourceBundle.class);
   return this;
 }

XHTML web view pages

What files need to be put in the /WEB-INF directory?

The following file types should be saved under the /WEB-INF directory
  • Templates
  • Includes
  • Error pages
Having these files in the /WEB-INF directory prevents the end user from being able to open them directly by entering its URL in the browser address bar. The normal pages and template clients which are intended to be accessed directly must not be placed in /WEB-INF folder.

The composite component files are also not supposed to be publicly accessible, however they are by specification required to be placed in /resources folder which is by default publicly accessible. If you make sure that you access all resources using the provided components so that they are never accessed by /resources in URL (but instead by /javax.faces.resource), then you can add a security constraint to the web.xml to block all public access to the /resources folder. However doing this blocks access from 3rd party libraries like DHTMLX that use javascript to set paths to image locations.

Avoid using the bindings attribute

In JSF 2 it is considered bad practice to use the bindings attribute to bind UI components to bean properties and is recommended to avoid doing this. There are careful considerations to take into account if you do decide to use a binding attribute in particular the scope of the bean should be request scope and not anything more broader than that. The following links have more detail on this topic:

preRenderView

In JSF 2 you can attach a javax.faces.event.PreRenderViewEvent system event to perform custom task before a view root (JSF page) is displayed. The following JSF snippet shows an example of this event being declared:
   <f:event listener="#{myBean.myMethod}" type="preRenderView" />
Use the preRenderView event when you want to execute a method during the beginning of the render response phase, right before the HTML is been rendered. This is particularly useful if you want to perform actions based on model values set by during update model values phase. The preRenderView event is invoked on every HTTP request including ajax requests. You can if necessary add a check on FacesContext#isPostback() to perform the action on initial request only.

Exception handling and Error pages

Service layer

Any exception thrown inside the business logic layer should be caught and handled accordingly and if necessary wrapped in a custom exception and thrown back to the presentation layer. Although try to handle the exception in the service layer otherwise there is extra work for the presentation layer to do in order to to catch all exceptions and to send a meaningful message back to the user.

A common Spring JDBC exception is the EmptyResultDataAccessException runtime exception, this should be caught and handled in the data access layer. This exception is thrown when a result was expected to have at least one row (or element) but zero rows (or elements) were actually returned. The DAO class should catch the EmptyResultDataAccessException exception, wrap it in a custom exception and throw it back to the service class. The service class can do one of the following:
  • Catch the custom exception and using Guava's API return an absent Optional of the type that is expected in the return. 
  • Throw the custom exception back to the GUI. Preferably avoid doing this, it just means the GUI has to catch the exception and decide what to do with it.
  • Return null. This should be the last resort and preferably you should never do this. The calling class would have to handle cases where there is null and since it is a runtime exception it won't always be apparent. Read the article mentioned above for more detail.

Presentation layer

The methods within the JSF managed beans should not throw any exceptions but should catch and handle all checked exceptions so that an appropriate message or response is sent back to the user. Although in some cases it may be necessary to throw an exception, a typical example would be when the IOException is thrown by the FacesContext when redirecting to a page.

FullAjaxExceptionHandlerFactory

  • The FullAjaxExceptionHandler will transparently handle exceptions during ajax requests exactly the same way as exceptions during synchronous (non-ajax) requests.
  • The FullAjaxExceptionHandler does not deal with normal (non-ajax) requests at all. To properly handle JSF and EL exceptions on normal requests as well, you need an additional FacesExceptionFilter.
  • Read this blog entry for more detail on handling exceptions during JSF ajax request.

FacesExceptionFilter

The FacesExceptionFilter will solve 2 problems with exceptions thrown in JSF methods.
  1. Mojarra's FacesFileNotFoundException needs to be interpreted as 404. We are using MyFaces so this shouldn't apply to us.
  2. Root cause needs to be unwrapped from FacesException and ELException to utilize standard Servlet API error page handling. Without this filter RuntimeExceptions would end up in an generic HTTP 500 error page instead of the one specific to the exception.

Session expiry

  • Managed beans (especially Session and Application scoped beans) should handle scenarios where a user tries to access a page that hasn't been fully initialised. This could happen if the user navigated to the page by another means other than its intended one, example by entering in the URL to the page directly in the browser and by-passing any initialisation methods, or when the users session has expired and they are forced to log back into the application. In this case they will be redirected back to the page they were currently working on.
  • One way to handle this scenario is to add a preRenderView event to the page that checks whether or not the page has been initialised correctly. If not you can redirect to another page that will be or if possible you can initialise the variable that need to be initialised.
  public void preRenderView() throws IOException {
   if (myObject == null) {
     LOG.debug("Expected variable to be initialised, possibly cleared from session on expiry. Redirecting to home page");
     Faces.redirect("home.xhtml");
   }
 }

Caching

Spring Caching

The Spring caching abstraction applies caching to Java methods, reducing the number of executions based on the information available in the cache. That is, each time a targeted method is invoked, the abstraction will apply a caching behavior checking whether the method has been already executed for the given arguments. If it has, then the cached result is returned without having to execute the actual method, if it has not, then the method is executed, the result cached and returned to the user so that the next time the method is invoked, the cached result is returned. This way, expensive methods (whether CPU or IO bound) can be executed only once for a given set of parameters and the result reused without having to actually execute the method again. The caching logic is applied transparently without any interference to the invoker.

Ehcache

Ehcache is an open source, standards-based cache for boosting performance, offloading your database, and simplifying scalability. It's the most widely-used Java-based cache because it's robust, proven, and full-featured. I configured the Spring cache to use Ehcache to cache static data that is often requested by the UI. Annotations are used on the DAO methods in order to cache the results or to clear out the elements stored in a cache.
  
    <cache:annotation-driven cache-manager="ehCacheManager" />

    <bean id="ehCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager" 
        p:cache-manager-ref="ehcache"/>
        
    <bean id="ehcache" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" 
        p:config-location="classpath:service-ehcache.xml"/>

Browser Caching

CacheControlFilter

If you have turned off caching in the browser for your web application than you will notice that the back button in a browser would request the page from the server.
  • If the page was stored in the session than JSF will return whatever is in the session otherwise it would fetch a new page. 
  • If the page was cached the browser would fetch the page from the browsers cache.
If you had a view scoped bean and caching was enabled you would notice that ajax calls won't be remembered in a browsers back button history. If you want that page added to the browsers cache you would need to use a non-ajax request.

I use OmniFaces CacheControlFilter which is has more control over how cache-related headers of the response are handled, web.xml:
 <filter>
  <filter-name>cache30min</filter-name>
  <filter-class>org.omnifaces.filter.CacheControlFilter</filter-class>
  <init-param>
    <param-name>expires</param-name>
    <param-value>30m</param-value>
  </init-param>
</filter>

<filter-mapping>
  <filter-name>cache30min</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

Static Resource Caching

The JSF resource handler automatically caches resources (placed in the /resources directory and referenced via , and and thus not via the plain HTML way). To satisfy Google recommendations you could set the Expires date a bit further in the future. It defaults to 7 days (604800000 milliseconds) while performance testing tools like Google Page Speed and Yahoo YSlow recommends a minimum of 30 days (2592000000 milliseconds). In MyFaces you can do this by adding the following context parameter to your web.xml file:
  
  <context-param>
   <param-name>org.apache.myfaces.RESOURCE_MAX_TIME_EXPIRES</param-name>
   <param-value>2592000000</param-value>   
 </context-param>
If you'd like to force reloading by resources because you changed them, then use resource library versioning. While developing you may set the cache headers to have no cache but in production you would want to cache resources. In this case when resources have changed you would want to increment the version in the resource folder. Development environment could either set the RESOURCE_MAX_TIME_EXPIRES parameter to 0 or it should have the following head meta attributes defined in the Facelets template:
 <meta http-equiv="Pragma" content="no-cache" />
 <meta http-equiv="Expires" content="0" />
 <meta http-equiv="cache-control" content="no-cache, no-store, must-revalidate" />

JSF Component Caching (OmniFaces o:cache)

The OmniFaces component allows to cache a fragment of rendered markup. The first request for a page that has this component on it will cause this markup to be put into the cache. Then for subsequent requests the cached content is used directly and none of the components, backing beans and services that were used to generate this content in the first place will be consulted.

UnmappedResourceHandler

This ResourceHandler implementation allows the developer to map JSF resources on an URL pattern of /javax.faces.resource/* (basically, the value of ResourceHandler.RESOURCE_IDENTIFIER) without the need for an additional FacesServlet prefix or suffix URL pattern in the default produced resource URLs, such as /javax.faces.resource/faces/css/style.css or /javax.faces.resource/css/style.css.xhtml. The OmniFaces resource handler UnmappedResourceHandler will produce unmapped URLs like /javax.faces.resource/css/style.css. This has the major advantage that the developer don't need the #{resource} EL expression anymore in order to properly reference relative URLs to images in CSS files.

Our web project is using 3rd party libraries (like DHTMLX) that reference images without the #{resource} EL expression the UnmappedResourceHandler comes in handy as we don't have to edit these 3rd party libraries to get them to work.

The UnmappedResourceHandler does not support the library attribute, therefore this:
  <h:outputStylesheet library="css" name="style.css" />
Would now be:
  <h:outputStylesheet name="css/style.css" />
Because the library attribute is not supported neither is library versioning. 

UI dropdown menus

Since JSF 2.0 there's no need anymore to provide a SelectItem[] or List, a T[] and List are accepted as well and you can access the current item by var attribute). Here is an example of how you could use Enums for the dropdowns:
Enum class (should be in its own a separate file):
 public enum Language {
  ENGLISH("english"),
  SPANISH("spanish");

  private String label;

  private Language(final String label) {
    this.label = label;
  }

  public String getLabel() {
    return label;
  }
}
Xhtml page (notice the label is being read from the resource bundle):
 <p:selectOneMenu id="languageSel" value="#{userDetails.language}">
  <f:selectItems value="#{userDetails.languages}" var="lang" itemValue="#{lang}" itemLabel="#{msg[lang.label]}" />
 </p:selectOneMenu>
JSF managed bean:
private Language language;

public Language getLanguage() {
  return language;
}

public void setLanguage(final Language language) {
  this.language = language;
}

public Language[] getLanguages() {
  return Language.values();
}

Choosing between action and actionListener

actionListener

Use actionListener if you want have a hook before the real business action gets executed, e.g. to log it, and/or to set an additional property (by ), and/or to have access to the component which invoked the action (which is available by ActionEvent argument). So, purely for preparing purposes before the real business action gets invoked. The actionListener method has by default the following signature:
import javax.faces.event.ActionEvent;
// ...
public void actionListener(ActionEvent event) {
  // ...
}
And it's supposed to be declared as follows, without any method parentheses:
 <h:commandXxx ... actionListener="#{bean.actionListener}" />
Note that you can't pass additional arguments by EL 2.2. You can however override the ActionEvent argument altogether by passing and specifying custom argument(s). The following examples are valid:
<h:commandXxx ... actionListener="#{bean.methodWithoutArguments()}" />
<h:commandXxx ... actionListener="#{bean.methodWithOneArgument(arg1)}" />
<h:commandXxx ... actionListener="#{bean.methodWithTwoArguments(arg1, arg2)}" />
(note the importance of the parentheses in the argumentless method, if they were absent, JSF would still expect a method with ActionEvent argument)

action

Use action if you want to execute a business action and if necessary handle navigation. The action method can (thus, not must) return a String which will be used as navigation case outcome (the target view). A return value of null or void will let it return to the same page and keep the current view scope alive. A return value of an empty string or the same view ID will also return to the same page, but recreate the view scope and thus destroy any currently active view scoped beans and, if applicable, recreate them.

The action method can be any valid MethodExpression, also the ones which uses EL 2.2 arguments, for example:
 <h:commandLink value="submit" action="#{bean.edit(item)}" />
With this method:
 public void edit(Item item) {
  // ...
 } 
Note that when your action method solely returns a string, then you can also just specify exactly that string in the action attribute. Thus, this is totally clumsy:
 <h:commandLink value="Go to next page" action="#{bean.goToNextpage}" />
With this senseless method returning a hardcoded string:
 public String goToNextpage() {
  return "nextpage";
 }
Instead, just put that hardcoded string directly in the attribute:

 <h:commandLink value="Go to next page" action="nextpage" />
Please note that this in turn indicates a bad design: navigating by POST. This is not user nor SEO friendly and is supposed to be solved as:
 
 <h:link value="Go to next page" outcome="nextpage" />

Invocation order

The actionListeners are always invoked before the action in the same order as they are been declared in the view and attached to the component. The following example will invoke Bean#listener1(), SomeActionListener#processAction(), Bean#setProperty() and Bean#submit() in this order:
 <h:commandLink value="submit" actionListener="#{bean.listener1}" action="#{bean.submit}">
   <f:actionListener type="com.example.SomeActionListener" />
   <f:setPropertyActionListener target="#{bean.property}" value="some" />
 </h:commandLink>

Exception handling

The actionListener supports a special exception called AbortProcessingException. If this exception is thrown from an actionListener method, then JSF will skip any remaining action listeners and the action method and proceed to render response directly. You won't see an error/exception page, JSF will however log it. This will also implicitly be done whenever any other exception is being thrown from an actionListener. So, if you intend to block the page by an error page as result of a business exception, then you should definitely be performing the job in the action method.

If the sole reason to use an actionListener is to have a void method returning to the same page, then that's a bad one. The action methods can perfectly also return void, on the contrary to what some IDEs let you believe via EL validation. Note that the PrimeFaces showcase examples are littered with this kind of actionListeners over all place. This is indeed wrong. Don't use this as an excuse to also do that yourself.

Client-side vs Server side State saving

HTTP is stateless and JSF is stateful. The JSF component tree is subject to dynamic (programmatic) changes. JSF simply needs to know the exact state as it was when the form had been displayed to the enduser, so that it can successfully process the whole JSF lifecycle based on the information provided by the original JSF component tree when the form has been submitted back to the server. The component tree provides information about the request parameter names, the necessary converters/validators, the bound managed bean properties and action methods.

Saving state on the client results in less of a load on the server at the expense of additional network traffic. This is because by default, client side is stored as a large hidden (javax.faces.ViewState) input field in web browsers. Saving state on the client also works better in failover situations because even if the server is down, the state won't be lost. Setting to client increases the network bandwidth usage but decreases the server memory usage, it also prevents ViewExpiredExceptions when the session has expired or when the client opens too many views. Using HTTP compression like gzip reduces the size of the HTTP message. When the state saving is set to client, then JSF won't store anything in session. You can do that by the following context param in web.xml:
 <context-param>
   <param-name>javax.faces.STATE_SAVING_METHOD</param-name>
   <param-value>client</param-value>
 </context-param>
It will then be serialized to an encrypted string in a hidden input field with the name javax.faces.ViewState of the form.

Resource bundle

The following snippets of code can be used to access a resource bundle property from a JSF managed bean.

faces-config.xml:
 <resource-bundle>
   <base-name>com.mydomain.jsf.messages.ApplicationResources</base-name>
   msg
 </resource-bundle>

Request, Session, Application Scoped Managed Bean

 @ManagedProperty("#{msg}") // bundle
 private ResourceBundle text; // +setter
 
 @ManagedProperty("#{msg['some.key']}") // key
 private String someKey; // +setter
 
 public void someAction() {
   String someKeyInBundle = text.getString("some.key");
   // ... 
 }

View Scoped Managed Bean

 @ManagedProperty("#{msg}") // bundle
 private transient ResourceBundle text; // +setter
 
 @ManagedProperty("#{msg['some.key']}") // key
 private transient String someKey; // +setter
 
 public void someAction() {
   String someKeyInBundle = text.getString("some.key");
   // ... 
 }
 private Object readResolve() {
   FacesContext context = FacesContext.getCurrentInstance();    
   myBundle = context.getApplication().evaluateExpressionGet(context, "#{text}", ResourceBundle.class);
   return this;
 }

Alternative means without injection and can be used in all scopes

  public void someAction() {
   FacesContext context = FacesContext.getCurrentInstance();
   
   // bundle
   ResourceBundle text = context.getApplication().evaluateExpressionGet(context, "#{msg}", ResourceBundle.class);
   String someKeyInBundle = text.getString("some.key");
 
   // specific key
   String someKey = context.getApplication().evaluateExpressionGet(context, "#{msg['some.key']}", String.class);
 
   // ... 
 }

OmniFaces Messages helper

The message resolver is registered in a custom web listener:
  @WebListener
  public class MyWebListener implements ServletContextListener {

    @Override
    public void contextDestroyed(ServletContextEvent arg0) {
      // empty method
    }

    @Override
    public void contextInitialized(ServletContextEvent arg0) {
      setMessageResolver();
    }

   /**
    * Set the message resolver to the ApplicationResources resource bundle. The
    * resolver can only be set once which is why it is done in this listener
    * class triggered by the contextInitialized method
    */
    private void setMessageResolver() {
      Messages.setResolver(new Messages.Resolver() {
        private static final String BASE_NAME = "com.mydomain.jsf.messages.ApplicationResources";

        public String getMessage(final String message, final Object... params) {
            final ResourceBundle bundle = ResourceBundle.getBundle(BASE_NAME, Faces.getLocale());
            if (bundle.containsKey(message)) {
                return MessageFormat.format(bundle.getString(message), params);
            }
            return MessageFormat.format(message, params);
        }
      });
    }
  }
Example to add a message:
  Messages.create("key").error().add();

Navigation

There are a number of options to choose from when navigating from one page to another using a link or a button. The below offers some guidelines to a few of them.

When navigating from one page to another using a link you would normally do this with an action or an URL. Whenever possible use a link that will issue a GET request and redirect the browser to the URL. At the very least these links will make the page bookmarkable.

h:link

Clicking on this link issues a bookmarkable GET request. It can also be achieved with the PrimeFaces p:link which is an extension to the h:link tag.

xhtml page:
<h:link value="Link 1" outcome="home-page">
Outcome maps to a navigation rule in the faces-config.xml file. You don't need to specify a redirect element in the navigation rule.

faces-config.xml:
<navigation-case>
 <from-outcome>home-page</from-outcome>
 <to-view-id>/home.xhtml</to-view-id>
 <redirect />
</navigation-case>

h:outputLink

Clicking on this link issues a bookmarkable GET request.

xhtml page:
 <h:outputLink value="#{request.contextPath}/home.xhtml">Link 2</h:outputLink>

h:commandLink

By default clicking on this link issues a non-bookmarkable POST request. It can also be achieved with the Primefaces p:commandLink which is an extension to the h:commandLink tag. The h:commandButton works in a similar way.

xhtml page
In this example notice how the action is not bound to a managed bean method but uses a URL instead:
<h:form>
  <h:commandLink value="Link 3" action="/home.xhtml" />
</h:form>
In order to make the above link bookmarkable you would either need to call an action method that returns a faces-config navigation mapping that has a redirect element on it or amend the code so that you append redirect to the end of the URL like the following:
<h:form>
  <h:commandLink value="Link 3" action="/home.xhtml?faces-redirect=true" />
</h:form>

p:menuitem

The Menuitem is used by various menu components of PrimeFaces. You can either specify a URL to a page you wish the user to be redirected to or you specify an action method on a managed bean that will redirect to a page based on the navigational rules defined in the faces-config file. Both mechanisms would generate bookmarkable links. The following example uses the URL attribute:

xhtml page:
<p:menuitem value="Link" url="/home.xhtml" />

No comments: