Wednesday, February 29, 2012

JSF 2 - Conditionally Skip Validation

The validated model update is a major foundation of Java Server Faces. It eases the development of complex web forms and together with JPA and Bean Validation it enforces best practices like database consistency and appropriate GUI behaviour, security etc. In JSF additional measures are necessary to skip the basic validation step, for instance in use cases with sub dialogs or wizards.
This article describes, how to update the model but skip the JSF validation in dependence of which button is clicked.

Why would anyone like to skip validation?

I give you one example. We often build heavy weight forms in our business apps - so we are not the cool guys in this cartoon. This are real business requirements and even if we liked to we cannot always change customers use cases.
Such forms not only contain basic input fields, selections etc., but there are also components like Add / Select..., where you go to a searchable list, select one or multiple items and come back.
This is a typical requirement in modern web application with rich visuals - so open a JavaScript based modal subdialog and you are done!? We mainly develop application for the e-Government. Currently this implies in Germany:
All functions must work with disabled JavaScript.
You find this very often in tenders for eGov business apps and it originates in the BITV history and misinterpretations. So we have to solve this without JavaScript, possible steps:
  • action Add / Select..., we must temporarily leave the form
  • apply model updates without validation: ignore invalid input and empty required fields
  • go to a subdialog, come back with the selected data
  • finally Save, but with complete form validation

Conditional Validation - First Considerations

The often recommended <h:commandButton immediate="true"> skips validation but doesn't help here, because no model updates are done either. You can use this feature for Cancel / Reset actions, but not for this use case.

The first solution that comes into mind is to add a parameter to the command action, which is possible since JSF 2:
<h:commandButton action="#{bean.addItem"} value="Add...">
   <f:param name="skipValidation" value="true"/>
</h:commandButton>
Other new JSF 2 features that help here are f:validateBean and f:validateRequired with a dynamic disabled attribute or @SkipValidation from ExtVal. I see multiple problems with this solution:
  • JavaScript necessary
    • With basic JSF components it was always quite clear which components can be used without JS, h:commandButton was one of them. But the new feature f:param changes this.
  • not secure
    • you can skip validation for all actions, even for unintended like saving:
    • simply add skipValidation=true as parameter via a tool, e.g.:
    • with addons like Firefox Tamper Data you can intercept and change POSTs
  • not a global solution
    • if you need this feature for many forms and components you have to duplicate code

JavaScript - resulting HTML for above example:
<input type="submit" name="liste:j_idt90" value="Add..." class="button"
    onclick="mojarra.jsfcljs(document.getElementById('liste'),{'liste:j_idt90':'liste:j_idt90','skipValidation':'true'},'');return false" />

 

Working Solution

The idea with custom validators is good but we need to find an alternative trigger and provide a global solution for this. Even if I don't like the JS-nature of h:commandButton parameters I understand it's technical reasons. Where can we add information to a button, so that the framework can decide if this should be validated or not?

We define a rule:

All commandButton ids that require validation start with "do" - for instance "doSave".

<h:commandButton action="#{bean.doSave}" id="doSave" value="Save" />
That means, actions with "do" as prefix behave like regular JSF, actions without this prefix are not validated.
If the form has the id="edit" then JSF generates this HTML:
<input id="edit:doSave" type="submit" name="edit:doSave" value="Save" />
If you look into the POST, again with Tamper Data:

The advantages of this rule:
  • we can use this "do" as global (or local) validation trigger (see ValidatorUtil below)
  • you cannot change the behaviour, if you add edit:Save=Save instead of edit:doSave=Save nothing happens, JSF will not execute the action
  • we cannot change the rule to something like "actions with <whatever>" are not validated, again you could add POST attributes that prevent validation for unintended cases

Implementation


The implementation consists out of an utility class, two slightly enhanced global validator classes and the validator declaration in app/src/main/webapp/WEB-INF/faces-config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<faces-config xmlns="http://java.sun.com/xml/ns/javaee"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_2_0.xsd"
 version="2.0">
...
 <validator>
  <validator-id>javax.faces.Bean</validator-id>
  <validator-class>app.controller.util.validator.SkipBeanValidator</validator-class>
 </validator>
 <validator>
  <validator-id>javax.faces.Required</validator-id>
  <validator-class>app.controller.util.validator.SkipRequiredValidator</validator-class>
 </validator>
...
</faces-config>

Enhanced global Bean Validator app.controller.util.validator.SkipBeanValidator:
package app.controller.util.validator;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.BeanValidator;

public class SkipBeanValidator extends BeanValidator {

 @Override
 public void validate(final FacesContext context, final UIComponent component, final Object value) {
  if (ValidatorUtil.check(context)) {
   super.validate(context, component, value);
  }
 }

}

Enhanced global Required Validator app.controller.util.validator.SkipRequiredValidator:
package app.controller.util.validator;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.validator.RequiredValidator;

public class SkipRequiredValidator extends RequiredValidator {

 @Override
 public void validate(final FacesContext context, final UIComponent component, final Object value) {
  if (ValidatorUtil.check(context)) {
   super.validate(context, component, value);
  }
 }

}

Utility class app.controller.util.validator.ValidatorUtil:
package app.controller.util.validator;

import java.util.Map;

import javax.faces.context.ExternalContext;
import javax.faces.context.FacesContext;

public final class ValidatorUtil {

 private static final String VALIDATE = "VALIDATE";

 public static boolean check() {
  return check(FacesContext.getCurrentInstance());
 }

 public static boolean check(final FacesContext context) {
  final ExternalContext externalContext = context.getExternalContext();
  final Object validate = externalContext.getRequestMap().get(VALIDATE);
  if (validate != null) {
   return (Boolean) validate;
  }
  for (final Map.Entry<String, String[]> requestParameters : externalContext.getRequestParameterValuesMap()
    .entrySet()) {
   final String key = requestParameters.getKey();
   if (key.contains(":do")) {
    externalContext.getRequestMap().put(VALIDATE, Boolean.TRUE);
    return true;
   }
  }
  externalContext.getRequestMap().put(VALIDATE, Boolean.FALSE);
  return false;
 }

}

 

Conclusions

Please feel free to comment if you recognize problems with this solution or suggest alternatives. Even though I have some years under the belt with JSF - you should always be careful with such framework adaptions. Automatic validation is a foundation of JSF functionality and security. JSF is very extensible - but not all what can be done should be done. In feature JSF releases I would like to find a ready to use solution for this problem.

Monday, January 16, 2012

Java verbosity, JEE and Lombok

JEE and Lombok are the new Dream Team! Lombok isn't exactly new technology. We use it for some time now to get rid of some Java verbosity, but this library had some major problems that made it nearly unusable in the past. After updating our project settings and Eclipse IDEs we discovered major improvements in Lombok. Let me explain the benefits of Lombok with JPA Entities and JAXB Content Objects and what has changed.

Java Persistence API (JPA)

I really like JPA and the advancements in recent versions. JPA standardizes long proven concepts and fits perfectly into the JEE framework stack with Validation, CDI, EJB and JSF. The Entity Manager APIs and especially the Entity mapping Annotations are mature and powerful. We even mapped some very weird legacy Database schemes without too many JPA provider specific extensions or hacks. Small example Entity with some JPA Annotations:
@Entity
@Table(uniqueConstraints = @UniqueConstraint(columnNames = "name"))
public class Role extends BaseEntity {

 // simple, no further annotations necessary
 private boolean active;

 // additional settings, used for database constraints and form validation
 @NotNull
 @Size(min = 3, max = 50)
 private String name;

 // more complex example
 @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
 @MapKeyColumn(length = 32)
 @MapKeyEnumerated(EnumType.STRING)
 @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
 private Map<GroupCategory, Group> groups;

 // start noise...
 public boolean isActive() {
  return this.active;
 }
 public void setActive(final boolean active) {
  this.active = active;
 }
 // etc. etc.

JPA Entities adapt the well known JavaBean conventions with it's setters and getters.
Hey...what about concurrency?
That JavaBeans are the conceptual counterpart to the immutable trend, they do it wrong!
The JPA architecture prevents such problems, e.g. through unshared managed Entities and Transaction handling. That means for instance that the EntityManager isn't shared between threads / requests, that even the 2nd level Cache only stores serialized Entity representations, that you get Concurrent Modification Exceptions and transaction rollbacks if two Entities persist at the same time to the same datastore record etc.

Java verbosity

My major problem with the JavaBeans style is not the mutability concept but the verbosity in Java. Recently there was an article that outlines quite good what I think about many of those "I can't stand Java verbosity anymore" arguments, often used as major justification for learning a new language. You should learn new languages and concepts, but this is really a terrible reason. You can do really well with good structured APIs. Java _is_ more verbose, but at the same time often very readable, even for none Java developers or without the knowledge of mystique DSLs and operators.

Project with many JavaBeans
But there are different kinds of verbosity - for me the IDE code generation of setters and getters that represent 2/3 of a class source is definitely superfluous verbosity that doesn't improve the readability at all. On top of that you often have to provide Javadoc and Tests for this stuff. Why that? Because if you work in customer projects you really want to prevent a discussion in the approval phase whether 100% test and documentation coverage is really a reasonable objective. Today these code metrics are transparent to the customer, tools like Sonar make that simple. But you have to interpret such metrics in context and some people that think in black and white have difficulties with that, especially when they sit on the demanding side.


For me a Java language extension for less verbose setters and getters is at least as important as Lambdas and such an Compiler addition should be much simpler. JavaBeans conventions are as old as SAM types and widely used. I really cannot understand why this isn't a hot Java language topic, especially in combination with many JavaBeans-focused JEE patterns and the JEE resurrection. Many modern languages support alternative concepts.

Lombok to the rescue

With Lombok you can get rid of all those setters and getters through the addition of two annotations:
  • @lombok.Getter
  • @lombok.Setter
The Lombok compiler extension than automatically generates all setters and getters for none-static fields:
@Entity
@XmlRootElement
@Getter
@Setter
public class Role extends BaseEntity {

 @Size(max = 1000)
 private String description;

 ... // no trivial setters and getters follow!
The generated JVM Bytecode methods don't differ from ordinary getters and setters with classic noisy source code (only some details like debug line numbers). This is fully transparent to all other frameworks. Lombok isn't required as runtime library. So you can also use this e.g. for JAXB Content Objects (tutorial) which also heavily follow the JavaBeans style. The above example contains @Entity and @XmlRootElement, that means this JavaBean is usable for O/R datastore persistence as well as XML marshalling and unmarshalling without any boilerplate code.

You can use your own setters and getters if more logic is necessary (e.g. lazy initialize empty fields), Lombok will skip the generation of existing methods. You can also annotate single fields instead of the whole class, e.g. the BaseEntity in the above example contains an auto generated id and doesn't provide an id setter. JPA doesn't really need the setters for populating the Entity:
@MappedSuperclass
public abstract class BaseEntity implements Serializable {

 @Transient
 @Getter
 @Setter
 private boolean selected;

 @Id
 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ident_Seq")
 @Getter
 private Long id;

 ... // no trivial setters and getters follow!


That's all great, but by no means a new message - why I'm so excited about it?

We are using Lombok for some time now. Even if it's benefits are undeniable we often thought about removing it from our projects. The problem was, that Lombok had many problems with the Eclipse auto format features (I heard from problems with other IDEs too).
If you used certain Save actions than you got Eclipse errors and jumbled invalid source code. The auto formatting and sorting often didn't work at all with Lombok annotations in place.
So... deactivate auto format actions?!
Better not do that! If you develop applications in teams with multiple developers you commonly use a source version control system like SVN, Git etc. It's very important that you can follow code changes (who changed what / why / when) in the history. If you reformat half the code with your flavor-of-the-day code style you lose this important information. It's important to have Programming Style Guidelines in the company and for the project but you must also enforce them before each checkin. IDE Save actions with preconfigured auto format rules are very powerful for this.

So this is not an option, but I was surprised how smooth many things work after our recent IDE and library updates for a project. A short look into the Lombok release notes shows severe activities around fixing those IDE problems. We still have some minor issues if you have own methods in your class and method sorting active, but no real show stopper.

Don't overuse technology

Lombok has many other annotations and you may find some other interesting as well. I will not repeat the documentation here. Especially @ToString, @EqualsAndHashcode, @...Constructor and the aggregation @Data look promising for JPA Entities and JAXB Content Objects. They create toString(), equals(), hashCode() etc. for all none-static and none-transient fields.

Beware: The generated methods trigger the lazy loading of collection properties and also iterate over arrays and collections. This leads to endless cycles or you will load, print and compare half of your database if you have an object model with many associations. You can restrict the used fields for the method generation - but not with the @Data all-you-can-eat, which seems so convenient but is absolutely useless for Entities. We don't use this annotations but you can give it a try.

Very short Lombok Setup Guide

  • Download lombok.jar.
  • The JAR is executable, specifiy IDE location in the appearing Installer dialog. This will add Lombok as Java agent to Eclipse or other IDEs
    (for Eclipse these lines are added to eclipse.ini: -javaagent:lombok.jar, -Xbootclasspath/a:lombok.jar)
  • Add this to your Maven POM:
  •    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>0.11.2</version>
        <scope>provided</scope>
       </dependency>
    
  • profit