I believe its well established that Struts would benefit from a good validation framework (duh, right?). While there are a number of really good ideas floating around, some seem to be introducing unnecessary complexity (IMHO, of course) Getting to the point, I think constrained properties from the JavaBeans spec. provides a built in, flexible foundation for struts invoked validation. (http://java.sun.com/products/javabeans/docs/spec.html) key advantages: * support is provided by the standard java.beans package * it doesn't require learning anything proprietary (even though other solutions are very cool :) * its conceptually simple; ie. before accepting a new value, the bean/ActionForm's setter method(s) ensures its accepted by all registered listeners before commiting the value (java.bean.VetoableChangeListenter). * and finally, its wide open for extension (key since we can't predict all validation scenario's, right?) I also believe that integration with Struts could be pretty straight forward. In a nutshell, we could make 2 modifications ActionServlet: 1. Make it responsible for registering "change-listeners" 2. Make it handle PropertyVetoException(s) thrown by setter methods (primarily by turning them into ActionErrors) Prior to making a "formal" proposal for this, I thought it might be useful to consider the attached code fragments: Then again, maybe I'm way off and should be ignored- If I am, let me know.. Regards, Levi Cook
<struts-config> <!-- etc.. --> <form-bean name="myForm" type="MyForm"> <change-listener property="email" type="RequiredEmailAddressValidator" /> </form-bean> <!-- etc.. --> </struts-config>
import java.beans.* public class RequiredEmailAddressValidator implements VetoableChangeListener { public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException { String email= null; boolean emptyOk= false; try { email= (String) evt.getNewValue(); } finally { if(StringValidator.checkEmail(email, emptyOk) == false) throw new PropertyVetoException("some.msg.key", evt); } } }
import org.apache.struts.action.ActionForm; import java.beans.*; public class MyForm extends ActionForm { private static final String EMAIL_PROP_NAME= "email"; private String email; public String getEmailAddress() { return email; } public void setEmail(String newEmail) throws PropertyVetoException { vcs.fireVetoableChange(EMAIL_PROP_NAME, email, newEmail); email= newEmail; } private VetoableChangeSupport vcs= new VetoableChangeSupport(this); public void addEmailVetoableChangeListener(VetoableChangeListener lsnr) { vcs.addVetoableChangeListener(EMAIL_PROP_NAME, lsnr); } public void removeEmailVetoableChangeListener(VetoableChangeListener lsnr) { vcs.removeVetoableChangeListener(EMAIL_PROP_NAME, lsnr); } }
import java.beans.*; public class OptionalEmailAddressValidator implements VetoableChangeListener { public void vetoableChange(PropertyChangeEvent evt) throws PropertyVetoException { String email= null; boolean emptyOk= true; try { email= (String) evt.getNewValue(); } finally { if(StringValidator.checkEmail(email, emptyOk) == false) throw new PropertyVetoException("some.msg.key", evt); } } }
import org.apache.regexp.*; public final class StringValidator { /** * Check that String email is a valid email address. * emptyOK=true indicates that an empty String is valid * for this test. */ public static boolean checkEmail(String email, boolean emptyOK) { if ((emptyOK == true) && (KRStringValidator.isEmpty(email))) return true; else if (StringValidator.isValidEmail(email)) return true; else return false; } /** * Checks to see if a String is a valid email address. */ public static boolean isValidEmail(String email) { if(email == null) return false; try { RE reEmail = new RE("^.+\\@.+\\..+$"); if (reEmail.match(email) ) return true; else return false; } catch(RESyntaxException e) { System.out.println(e); return false; } } /** * Checks to see if a String is null or all spaces. */ public static boolean isEmpty(String s) { return ((s == null) || (s.trim().length() == 0)); } }