I was sitting at lunch eating a coney at Skyline in Cincinnati (not my favorite YUCK).... I from Tucson AZ, and I can't understand how Skylines draws such a crowd.
I was thinking how much I hate requiredif validator. I was also thinking that I don't like validatewhen much better.
Why write your own expression language? Why not use Ognl or JSTL?
I wrote my own version of validatewhen, and it uses JSTL lib from the Jakarta tag project. It took me about 20 minutes to write this. Short, sweet, and I'll never use validatewhen or requiredif again!
In my opinion, this new validate rule is much better than requiredif or validatewhen (at least the versions that I messed with). I can't believe someone did not think of tihs sooner.
It is also really easy to learn since you should know JSTL already.... Nothing special past that! (JSTL is easy and you have to learn it for JSP 2.0. You should be using JSTL tags in place of logic:* tags already.).
I call this new rule validateel (i have not thought of a better name for it yet... but I will, let me know if you think of one).
The nice thing about this rule is that it has access to the complete pageContext name space like any JSTL tag.
(more on this trick later).
Here is an example of using this rule to check to see if a passwordCheck field is equal to a password field as follows:
<field property="passwordCheck"
depends="validateel">
<arg0 key="inputForm.passwordCheck"/>
<var>
<var-name>test</var-name>
<var-value>
${value==form.password}
</var-value>
</var>
I created a FakePageContext class that takes a HttpServletRequest, and mocks up page context. I then add form to the fake page context as well as value inside of my new rule as follows:
PageContext pageContext = new FakePageContext(request);
String test = field.getVarValue("test"); //Get the test var (this is the JSTL expression)
pageContext.setAttribute("form",bean); //Map in the form
pageContext.setAttribute("field",field); //Map the field object (Just in case)
String value = ValidatorUtil.getValueAsString(bean, field.getProperty()); //Get the value of the property
pageContext.setAttribute("value",value); //Map the value into the page context.
The workhorse that actually does the JSTL expresion evaluation is from the JSTL lib. I just invoke it as follows:
result = (Boolean) ExpressionEvaluatorManager.evaluate("validateEL", test, Boolean.class, pageContext);
This was so easy... and IMHO it is better than requiredif and validatewhen. First... it uses JSTL which is an expression language people either know or should know (as it is required knowledge in JSP 2.0).
Here is the complete ValidateEL method and imports that implements this new validator rule.
import javax.servlet.http.HttpServletRequest;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import org.apache.commons.validator.Field;
import org.apache.commons.validator.ValidatorAction;
import org.apache.commons.validator.ValidatorUtil;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.validator.Resources;
import org.apache.taglibs.standard.lang.support.ExpressionEvaluatorManager;
/**
* @author rhightower
*
*/
public class CustomValidatorRules {
public static boolean validateEL(
Object bean,
ValidatorAction va,
Field field,
ActionErrors errors,
HttpServletRequest request) {
PageContext pageContext = new FakePageContext(request);
String test = field.getVarValue("test");
pageContext.setAttribute("form",bean);
pageContext.setAttribute("field",field);
String value = ValidatorUtil.getValueAsString(bean, field.getProperty());
pageContext.setAttribute("value",value);
Boolean result = Boolean.FALSE;
try{
result = (Boolean) ExpressionEvaluatorManager
.evaluate("validateEL",
test,
Boolean.class,
pageContext);
}catch (JspException je){
// TODO fix
je.printStackTrace();
}
boolean r = result.booleanValue();
if (r == false){
errors.add(
field.getKey(),
Resources.getActionError(request, va, field));
}
return r;
}
....
Here is the listing for the FakePageContext (it fakes the needed parts of the context and leaves the rest noops):
import java.io.IOException;
import java.util.Enumeration;
import java.util.Hashtable;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.PageContext;
/**
* @author rhightower
*
* To change the template for this generated type comment go to
* Window>Preferences>Java>Code Generation>Code and Comments
*/
public class FakePageContext extends PageContext {
HttpServletRequest request;
Hashtable map = new Hashtable();
public FakePageContext(HttpServletRequest request){
this.request = request;
}
/* (non-Javadoc)
* @see javax.servlet.jsp.PageContext#getAttribute(java.lang.String)
*/
public Object getAttribute(String key) {
return map.get(key);
}
/* (non-Javadoc)
* @see javax.servlet.jsp.PageContext#setAttribute(java.lang.String, java.lang.Object)
*/
public void setAttribute(String key, Object value) {
map.put(key, value);
}
/* (non-Javadoc)
* @see javax.servlet.jsp.PageContext#removeAttribute(java.lang.String)
*/
public void removeAttribute(String key) {
map.remove(key);
}
/* (non-Javadoc)
* @see javax.servlet.jsp.PageContext#getOut()
*/
public JspWriter getOut() {
return null;
}
/* (non-Javadoc)
* @see javax.servlet.jsp.PageContext#getSession()
*/
public HttpSession getSession() {
return request.getSession(true);
}
/* (non-Javadoc)
* @see javax.servlet.jsp.PageContext#getPage()
*/
public Object getPage() {
return null;
}
/* (non-Javadoc)
* @see javax.servlet.jsp.PageContext#getRequest()
*/
public ServletRequest getRequest() {
return this.request;
}
/* (non-Javadoc)
* @see javax.servlet.jsp.PageContext#getResponse()
*/
public ServletResponse getResponse() {
// no op
return null;
}
/* (non-Javadoc)
* @see javax.servlet.jsp.PageContext#getException()
*/
public Exception getException() {
// no op
return null;
}
/* (non-Javadoc)
* @see javax.servlet.jsp.PageContext#getServletConfig()
*/
public ServletConfig getServletConfig() {
return null;
}
/* (non-Javadoc)
* @see javax.servlet.jsp.PageContext#getServletContext()
*/
public ServletContext getServletContext() {
return null;
}
/* (non-Javadoc)
* @see javax.servlet.jsp.PageContext#forward(java.lang.String)
*/
public void forward(String arg0) throws ServletException, IOException {
//no op
}
/* (non-Javadoc)
* @see javax.servlet.jsp.PageContext#include(java.lang.String)
*/
public void include(String arg0) throws ServletException, IOException {
//no op
}
/* (non-Javadoc)
* @see javax.servlet.jsp.PageContext#initialize(javax.servlet.Servlet, javax.servlet.ServletRequest, javax.servlet.ServletResponse, java.lang.String, boolean, int, boolean)
*/
public void initialize(
Servlet arg0,
ServletRequest arg1,
ServletResponse arg2,
String arg3,
boolean arg4,
int arg5,
boolean arg6)
throws IOException, IllegalStateException, IllegalArgumentException {
//no op
}
/* (non-Javadoc)
* @see javax.servlet.jsp.PageContext#setAttribute(java.lang.String, java.lang.Object, int)
*/
public void setAttribute(String key, Object value, int scope) {
if (scope ==PageContext.PAGE_SCOPE){
map.put(key, value);
}else if (scope == PageContext.REQUEST_SCOPE){
request.setAttribute(key,value);
}else if (scope==PageContext.SESSION_SCOPE){
request.getSession().setAttribute(key,value);
}else if (scope==PageContext.APPLICATION_SCOPE){
//TODO fix
}
}
/* (non-Javadoc)
* @see javax.servlet.jsp.PageContext#getAttribute(java.lang.String, int)
*/
public Object getAttribute(String key, int scope) {
if (scope ==PageContext.PAGE_SCOPE){
return map.get(key);
}else if (scope == PageContext.REQUEST_SCOPE){
return request.getAttribute(key);
}else if (scope==PageContext.SESSION_SCOPE){
return request.getSession().getAttribute(key);
}else if (scope==PageContext.APPLICATION_SCOPE){
return null; //TODO fix
}else {
return null;
}
}
/* (non-Javadoc)
* @see javax.servlet.jsp.PageContext#removeAttribute(java.lang.String, int)
*/
public void removeAttribute(String key, int scope) {
if (scope ==PageContext.PAGE_SCOPE){
map.remove(key);
}else if (scope == PageContext.REQUEST_SCOPE){
request.removeAttribute(key);
}else if (scope==PageContext.SESSION_SCOPE){
request.getSession().removeAttribute(key);
}else if (scope==PageContext.APPLICATION_SCOPE){
//TODO fix
}else {
// no op
}
}
/* (non-Javadoc)
* @see javax.servlet.jsp.PageContext#getAttributeNamesInScope(int)
*/
public Enumeration getAttributeNamesInScope(int scope) {
if (scope ==PageContext.PAGE_SCOPE){
return map.keys();
}else if (scope == PageContext.REQUEST_SCOPE){
return request.getAttributeNames();
}else if (scope==PageContext.SESSION_SCOPE){
return request.getSession().getAttributeNames();
}else if (scope==PageContext.APPLICATION_SCOPE){
return null; //TODO fix
}else {
return null;
}
}
/* (non-Javadoc)
* @see javax.servlet.jsp.PageContext#getAttributesScope(java.lang.String)
*/
public int getAttributesScope(String arg0) {
// No op
return 0;
}
/* (non-Javadoc)
* @see javax.servlet.jsp.PageContext#findAttribute(java.lang.String)
*/
public Object findAttribute(String key) {
Object value = map.get(key);
if (value == null){
value = request.getAttribute(key);
}
if (value == null){
value = request.getSession().getAttribute(key);
}
if (value == null){
//TODO look it up in app scope
}
return value;
}
/* (non-Javadoc)
* @see javax.servlet.jsp.PageContext#handlePageException(java.lang.Exception)
*/
public void handlePageException(Exception arg0)
throws ServletException, IOException {
// No op
}
/* (non-Javadoc)
* @see javax.servlet.jsp.PageContext#handlePageException(java.lang.Throwable)
*/
public void handlePageException(Throwable arg0)
throws ServletException, IOException {
// No op
}
/* (non-Javadoc)
* @see javax.servlet.jsp.PageContext#release()
*/
public void release() {
// No op
}
}
For completeness...
I had to add this entry in validation-rules.xml
<validator name="validateel"
classname="ch15.CustomValidatorRules"
method="validateEL"
methodParams="java.lang.Object,
org.apache.commons.validator.ValidatorAction,
org.apache.commons.validator.Field,
org.apache.struts.action.ActionErrors,
javax.servlet.http.HttpServletRequest"
msg="errors.validateEL">
</validator>
It took me longer to write this blog entry then it did to write the above code. IMO if you are using requiredif or validatewhen, then you are wasting your time. Also, if you use the validator framework and you ever need to do a simple comparison of two fields... this is the way to go. With JSTL you have access to headers, attributes in session, request attributes, request parameters, and so much more.... Use this... it works and it is cool.
Here is how I would do the above in my own validate method of an ActionForm (for reference).
public class InputFormAll extends ValidatorForm {
...
public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
ActionErrors errors = super.validate(mapping, request);
if (!(password.equals(passwordCheck))){
errors.add(
"password",
new ActionError("errors.password.nomatch"));
}
return errors;
}
Using validateel is so much more terse!
${value==form.password}
and I am done!