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 

 

Reply via email to