Saturday, November 04, 2006

Wicket Validation Revisited; AJAX Comes to morons.org; Unit Testing

I wanted to take a second look at the way I was doing validation, in part because I still wanted to figure out how to get my validators to affect change in the model objects (e.g., if I want my "NoHtmlValidator" strip the HTML tags). I had also noticed that the way I constructed my validator was not strictly in-keeping with the way the Wicket folks wanted it done. There's a big, all-caps message for the validate method saying "THIS IS NOT PART OF THE PUBLIC API". One wonders why the method is public in that case.

The Wicket API for IValidator suggested extending CustomValidator to do a custom validator, but CustomValidator is deprecated. I guess their Javadoc is a bit outdated. Instead, the thing to do is to extend StringValidator, a class for which you can override the onValidate method. The main difference I can see is that this passes you both the FormComponent and the String data associated with the model for that component... saving you having to call component.getInput() yourself.

After hunting through the available completions for a bit, I found the methods setModelValue(String) and setModelValue(String[]) for setting a single-value attribute or a multi-value attribute respectively. This is one of those few times one is reminded that what we're actually always dealing with are strings sent via HTTP. Some things are single strings, like text fields and check boxes; some things are arrays of strings, like multi-select boxes.

Now the code for my NoHtmlValidator looks like this:


package net.spatula.news.ui.validators;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

import wicket.markup.html.form.FormComponent;
import wicket.markup.html.form.validation.StringValidator;

public class NoHtmlValidator extends StringValidator {

private static final long serialVersionUID = 1L;
private static final Pattern pattern = Pattern.compile(".*</?[-\\w]+[^>]+>.*",
Pattern.DOTALL);

public void onValidate(FormComponent component, String data) {

Matcher matcher = pattern.matcher(data);
if (matcher.matches()) {
error(component);
data = data.replaceAll("</?[-\\w]+[^>]+>", "");
component.setModelValue(data);
}
}

}


You can see I've moved the Pattern up into the class; there's no sense in spending the overhead of compiling that Pattern more than once. The key change here is that if you did include HTML tags in your field, the validator now strips those tags for you.

My next task was to get the Article Section select box (DropDownChoice in Wicket terms) populated. For this, I created a SectionService singleton class. Borrowing an idea from an Oracle JPA tutorial I also created a singleton ResourceManager class for creating the EntityManagerFactory. Provided anything needing an EntityManagerFactory gets it from the ResourceManager class, this has the effect of ensuring that across the application, I only ever create one EntityManagerFactory. This is definitely desirable while testing, since with the JPA RI (Glassfish), the JDBC connection pool is managed by the EMF. There's no sense in creating more than one of these.

I was able to fairly quickly create my service. Initially I had it return a List of KeyValuePairs for the section ID and the section name. It was trivial to populate the DropDownChoice. There was still a problem, however. My section names are fairly terse, and if I'm to have my readers able to submit articles at-will, they are going to need some guidance about which section they should use for their articles.

This seemed like a good job for AJAX.

Wicket has some built-in AJAX goodies that are perhaps not quite as easy to use as Google's web toolkit, but they have the advantage of being integrated into the framework. To get AJAX behaviour out of the Sections DropDownChoice, we add AjaxFormComponentUpdatingBehavior to the choice. It turns out that the easiest way to do this is with an anonymous inner class, as demonstrated on the Wicket Wiki. My code looks like this:


// In the Form class, declare the objects you are going to change from an AJAX action,
// in this case, a Label called sectionDescription.
private ArticleBean article = new ArticleBean();
private Label sectionDescription = new Label("sectionDescription", "");

// Later on as you're building your form...

EntityManager em = ResourceManager.getInstance().getEMF()
.createEntityManager();
List sectionsList = SectionService.getInstance()
.getSections(em);
em.close();

sectionDescription.setOutputMarkupId(true);
if (article.getSection() != null && article.getSection().getId() != 0) {
sectionDescription.setModelObject(article.getSection()
.getDescription());
}
add(sectionDescription);

DropDownChoice sectionChooser = new DropDownChoice("section",
new PropertyModel(article, "section"), sectionsList,
new ChoiceRenderer("name", "id"));
sectionChooser.add(new AjaxFormComponentUpdatingBehavior("onchange") {

private static final long serialVersionUID = 1L;

protected void onUpdate(AjaxRequestTarget target) {
sectionDescription.setModelObject(article.getSection()
.getDescription());
target.addComponent(sectionDescription);
}

});

add(sectionChooser);


So what's going on here? First, I get my List of sections from my service. You can see that I had to abandon KeyValuePairs for SectionBeans, since I needed not only the ID and name, but the description also. Then, we have to setOutputMarkupId(true) so that Javascript can manipulate the HTML object by name. Next there is code to statically render the section description if it is already known; for example, after a form submission in the case that the form needs to be re-displayed.

Then there is code to create the DropDownChoice. Because I want the rendered option values to correspond to the section IDs on the database, I use a ChoiceRenderer, giving it the names of the fields to access from the objects in the list to get the display text and the value to associate with that text as the DropDownChoice iterates through the list.

Finally, we add the AjaxFormComponentUpdatingBehavior to the DropDownChoice component. The "onchange" Javascript event is specified, and in the anonymous inner class extending AjaxFormComponentUpdatingBehavior, onUpdate is overridden. Just as we did when rewriting a field with a validator, we can rewrite the contents of the simple String model of the sectionDescription Label to the description of the SectionBean held in the Article. Notice that we're asking the article-- the backing model-- itself for this SectionBean object; we're not querying the component.

I happily loaded my web page and clicked on the drop-down. Nothing happened. I looked at the page source, and indeed there was Javascript markup on that <select> tag. I popped open the Javascript console and found it full of errors: "wicketAjaxPost is not defined." I looked closer at the source and sure enough, there was no script loading to define that function. After some digging, I came to realize that the culprit was Sitemesh. Wicket puts the declarations to load the Javascript code (which it serves out via its servlet) in the <head> section of the HTML document, which Sitemesh happily strips. The solution is to include a <decorator:head /> tag in the Sitemesh decorator at the end of its <head> section.

One thing I tried to do with AJAX in Wicket initially proved not to work. I wanted to make a <br /> tag come and go depending on whether a choice had been made in the drop-down. Unfortunately, there seems to be some problem with changing the value of setVisible() with AJAX, at least on a <br>. I ended up just putting the section description after the drop-down box, which turned out to be nicer anyway, since it doesn't change the vertical size of the table when the text gets rendered. UPDATE: it doesn't work because AJAX is operating on the client side; in other words, because the initial state is not visible, there's no markup sent for the <br /> to the client, which in turn means it's impossible for AJAX to tell where to put it when it's added. The solution is to wrap it in something that IS sent, like a containing <span> and repaint the <span>.

Next I want to start looking deeper at WicketTester, which helps you write unit tests for Wicket pages and forms, helping you test validators and page contents without having to run a servlet container. If it works, this could be the holy grail of web application UI testing, something that has never had a great solution. (And if you think HTTPUnit is a great solution, there is something the matter with you.) Maybe I can get a decent unit test for NoHtmlValidator going. It won't have been test-first, but the other validators can be now.

Labels:


Comments:
Hi Nick. I saw in the "About Morons.ORG" blurb that you were in the process of converting the site to Java technology. Would you be interested in a mention of this at Java.Sun.Com when you are ready?

Separatedly, please let me know if you are at all interested in trying GlassFish. Or if you tried and it didn't do what you wanted. Or any other feedback.

Thanks, - eduard/o
 
Hi, eduard/o,

In fact, morons.org went to Java about 3 years ago now, away from PHP. The PHP code had just become too unmanageable (and slow). I am currently in the process of taking the lessons learned over the last 3 years and doing a major refactoring of the site design using Resin Opensource, Wicket, Sitemesh and Glassfish's persistence.

I'll certainly take any publicity I can get, especially from java.sun.com ;-)

I'm fairly impressed with the JPA RI / Glassfish persistence so far (and as I've noted in other posts, I'm a fan of Toplink already). I'll be more impressed if the PostgreSQL sequence generation problems can be sorted out.

My main complaint about Glassfish/Toplink persistence is the same as it has been all along with Toplink: the obtuse and stupid error messages. See my other posting in the blog about error messages for more detail in that regard. Nothing gives me more heartburn than poorly-worded, unhelpful error messages, except maybe Apache projects' seemingly preferred way of dealing with exception conditions (e.printStackTrace()). That and really hot salsa.
 
Hi Nick,

I have a question about one point in your article, and I hope you can help me:

You say that
"I only ever create one EntityManagerFactory. This is definitely desirable while testing, since with the JPA RI (Glassfish), the JDBC connection pool is managed by the EMF. There's no sense in creating more than one of these."

BUT what can I do if I cant use only one name/password for the application, but every user needs to use his own name/password??

With Toplink I can create only on EntityManagerFactory which sets up a pool of connections with same iseris/password but I want for every user using my web application a different username/password.

Can you suggest something?

Thx,
Alex
 
I think you're confusing database authentication with user authentication. Normally you would want the database communication to be something that's authenticated between the web application and the database. Then your user authentication happens between the user and the application. I've never wanted to have every user have his own login to my back-end database.

What I do is store usernames and encrypted passwords in my USER table and authenticate the users against the data in that table.
 
Hi Nick,
thanks for answering!

But when you have only one database user you can only do application-based security.

In my case, we have a database shared by various applications, and it is part of the security concept that every user is logged on with his personal ID.

Best regards,
Alex
 
"There's a big, all-caps message for the validate method saying "THIS IS NOT PART OF THE PUBLIC API". One wonders why the method is public in that case."

In general, a method that ought to be private is public because it is being called from another package. This is an unfortunate limitation of Java at this time. I am told this problem is finally going to be addressed in Java 7. - Jonathan Locke
 
Post a Comment





<< Home

This page is powered by Blogger. Isn't yours?

Subscribe to Posts [Atom]