Tuesday, June 22, 2010

Conversion and Validation in JSF 2.0


Well, we have all the old stuff plus some new stuff here. The most remarkable additions I can see here are that a validator tag can now wrap a set of tags…not just be a child in one that needs a validator added to it. So your page can have default validators. Another addition that I don't entirely understand the syntax of yet is Java EE 6 Bean Validation. This seems very useful, but the syntax is a bit odd to me. But mostly this is because I am not familiar with defining custom annotations. Also they seem to have come up with a new usage of generics…I guess it is the same, but the usage bent my mind in a new direction.

So let me elaborate. ("Go right ahead!" you are probably thinking) Let's say I start by removing the 'validator="#{userBean.validateEmail}"' code from my h:inputText tag. I do this because when I get everything switched to Bean Validation, I will not need this JSF tag validation any more. I then put the custom annotation @Email above my UserBean.java property declaration for the "protected String email;" property. If I recompile at this point I get an error: UserBean.java:18: Cannot find symbol…symbol: class Email… So the compiler knows this is an annotation but it does not know about it yet…because I have not created that class yet.

Now I define the class that defines my annotation (code taken from (Ed Burns, 2010)):

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

@Documented
@Constraint (validatedBy = EmailConstraintValidator.class)
@Target ({ElementType.METHOD, ElementType.FIELD})
@Retention (RetentionPolicy.RUNTIME)
public @interface Email {
String message() default "{validator.email}";

    Class<?>[] groups() default {};

    Class<? extends ConstraintPayload>[] payload() default {};
}


If you have never defined your own annotation before there a lot of mysteries to unfurl in this class. The two bits I think are the most important is the part that points to the class where our validation code is stored (…validatedBy = EmailConstraintValidator.class…), and also the bit which stores what message will occur should the validation indicate that (in this case) our email is not valid.


 

OK: so now when I try to compile Email .java, I get a complaint that package javax.validation does not exist. This is my JSF 2.0 manual says the Constraint annotation lives. So that means there is a jar out there I need to find somehow. This is not surprising since my manual (Ed Burns, 2010) says that this is a Java EE 6 (container) thing and not just a JSF thing. So…where to look…I am using glass fish, so I imagine that Glass Fish knows about this jar. Maybe if I ask it nicely, it will tell me where it is kept.


 

OK, I went looking through administrator's console, and there is nothing jumping out at me…like "find a jar with a known class in it". Maybe JDev fine search will prove useful…well not so much. Of course I was trying to use the search files tool. Maybe there is a different tool?


 

Meanwhile I went on a manual hunt through all glassfish jar files with suspicious names. Like for example there is a javax.annotation.jar Well, then!! That certainly sounds promising! But when I used VIM to inspect the contents of this jar file…I found…no javax.annotation.Constraint!! Very odd. I also searched for javax and annotation in all the files under the glassfish installation directory. It was not there. So: maybe glassfish does not offer Bean Validation? I thought it did since it was Java EE 6. But maybe it is not the Enterprise edition or the weaseldoodle's edition or some-such. I will have to ask what is going on with this when I get out of the sky in back into internet areas.


 

OK: so this is a good URL to know to find this JAR… http://jcp.org/aboutJava/communityprocess/final/jsr303/index.html


 

Javax.constraints.ConstraintPayload.class seems to be a hard one to track down. The JSR says that the reference implementation is hibernate validator. When I open that up I see the source contains one jar called validation-api-1.0.0.GA.jar but this has no class in it called ConstraintPayload. It does have a "Payload" class in it however. So then I tried to do a mvn install on the online code for chapter 8 for (Ed Burns, 2010) and it all built ok. His source included references to ConstraintPayload…so I reasoned that he must have a different jar. So I looked in his pom.xml for this code and it referred to bean-validator.jar, and referred to org.glassfish (great! That is what I am using!) and also JBoss (darn…not what I am using) and something about Beta (yikes!!). Sure enough there is a bean-validator.jar in my glassfish distribution (yay!!) also with no mention of ConstraintPayload.class (damn, damn, damn). Apparently I have to download a jar from http://download.java.net/maven/2//org/glassfish/bean-validator/3.0-JBoss-4.0.0.Beta3A/bean-validator-3.0-JBoss-4.0.0.Beta3A.jar


 

Jeez! I am beginning to wonder whether they renamed ConstraintPayload.class to just Payload.class…


 

And of course…this is correct according to http://opensource.atlassian.com/projects/hibernate/browse/HV-319 "Hardy Ferentschik added a comment - 06/May/10 4:55 AM --javax.validation.ConstraintPayload got renamed into {javax.validation.Payload}} for the final release of the Bean Validation spec…" Well, OK then. So I am going with the jar that came with GlassFish!! That is: bean-validator.jar, and change my ConstraintPayload.class references to Payload.class.

So now the class that defines the Constraint annotation refers to the Validator. But the Validator class also makes reference to the annotation class. So neither one can finish compiling it seems…? This is kind of annoying…how does maven compile this shit as is? It must compile twice, the first time suspending failing compilation if there is this kind of cross-reference. It must be some kind of directive that is made just for annotations…OK: I'm a forgetful so-and-so: the class path must contain either jar's (that I knew) or directories that contain the base of packages you need during compilation. Interestingly I see that the java compiler compiled both files when I only explicitly asked for the compilation of one file:

In the C:\sandbox\jsf2-0book\facelets1\target\facelets1\WEB-INF\classes\com\jsfcompref\model directory, I issued a successful compilation command of EmailConstraintValidator.java, when it was dependent on another class called Email.java in the same directory. Email.java had not been compiled yet, but it was valid except for a reference to the EmailConstraintValidator.java class.

In the compilation command, javac -classpath jsf-api.jar;jsf-impl.jar;bean-validator.jar;..\..\.. EmailConstraintValidator.java, we see adding ..\..\.. gives visibility to the directory that contains the com\jsfcompref\model directory which is the base of the package that Email and EmailConstraintValidator declared as being located in.

2 comments:

jr said...

Hi,

Thanks for that. It meant I could do a successful build in 5minutes after changing ConstraintPayload to Payload.

Whether the build deploys and works is another matter.

Michael A. Fons said...

I hope it does work for you. I have had a bit of trouble with this bean validation stuff. But this JSF 2.0 book seems to have a version that works in their sample code...so I think it is possible.