I am new to this list so I apologize if I break any etiquette. I was thinking that I don't like validatewhen or requiredif so I wrote my own that uses JSTL EL. (I like the idea behind requiredif and validatewhen just not the implementation.) I believe this approach has several advantages over requiredif and validatewhen. I call this new rule validateel (I have not thought of a better name for it yet...).
Why write your own expression language? Why not use OGNL or JSTL EL? I think JSTL makes the most sense for the follwing reasons: 1) EASY TO LEARN The first advantage of this approach is it is easy to learn since developers know JSTL EL already. JSTL EL is easy to learn and you have to learn it for JSP 2.0 anyway. In fact, developers should be using JSTL tags in place of logic:* tags already. 2) ACCESS TO PAGE CONTEXT The nice thing about this rule is that it has access to the complete pageContext (Headers, Request Parameters, Session, the whole thing) name space like any JSTL tag. (more on this trick later). Since I am using JSTL EL I can make my expression as complex as need be, e.g., I can check to see if one date is before another or if a string starts with a certain substring. It is completely powerful, and yet very easy to learn and use. 3) VERY LITTLE CODE This rule was very easy to implement. It relies on the Jakarta JSTL EL implementation. I don't see the point in adding a new expression language just for Struts when Struts relies on JSP and Servlets and JSP will be using JSTL. (Less code to add means less code to maintain....) DETAILS: VALIDATOR RULE USAGE: 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>... RULE IMPLEMENTATION DETAILS: I created a FakePageContext class that takes an HttpServletRequest, and mocks up page context. I then add form object to the fake page context as well as value of the current field 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 bean 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. USES JSTL LIB EXPRESSION EVALUATOR MANAGER The workhorse that actually does the JSTL expression evaluation is from the Jakarta JSTL lib. I just invoke it as follows: result = (Boolean) ExpressionEvaluatorManager.evaluate("validateEL", test, Boolean.class, pageContext); Here is the complete ValidateEL method for the new validator rule. ... 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; it should also get the application context from the Globals key for the ServletContext): ... public class FakePageContext extends PageContext { HttpServletRequest request; Hashtable map = new Hashtable();//TODO: Change to hashmap or fasthash map public FakePageContext(HttpServletRequest request){ this.request = request; } public Object getAttribute(String key) { return map.get(key); } public void setAttribute(String key, Object value) { map.put(key, value); } public void removeAttribute(String key) { map.remove(key); } public HttpSession getSession() { return request.getSession(true); } public ServletRequest getRequest() { return this.request; } 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 get app scope from Globals key } } 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; } } 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 } } 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; } } 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; } //other methods are no ops It took me longer to write this email then it did to write the above code (including the rule itself and the example code) since the JSTL EL engine provided by Jakarta does all of the real work. 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. Plus, with JSTL EL you have access to headers, attributes in session, request attributes, request parameters, and so much more. I think there is room for a tag that uses OGNL. OGNL gets used by tapestry and WebWork2. Perhaps yet another validator rule could use the BSF (BeanScriptingFramework) this would allow the validate expression to be written in Rhino (JavaScript for Java), Jython (Python for Java), BeanShell, Perl and more. I think using BSF would be nice. You could have the client and server side validation using the same JavaScript code. I am willing to contribute the above as well as write a rule that uses BSF and OGNL. Thoughts? --Rick Hightower