Discussions

J2EE patterns: Reusable Data validation using EJB 2.0 Home Methods and BMP

  1. The pattern is based on EJB 2.0 Home methods and builds on the detail object pattern here.

    Once your EJB's have working detail object, clients of the EJB can get and set these objects we will need to write a validate Home method and a ValidationErrors class that can return potential validation errors:

    // some actual code is left out to save space.

    public class CustomCreateException extends CreateException {
      public CustomCreateException(ValidationErrors errors){}
    }

    public void ValidationErrors extends HashMap {
      //make our issues a HashMap of Strings..
      //in practice the "issue" can be a custom class
      //that stores issues.
      public void addIssue(String name,String issue) {
      }
    }

    public class BeanDetail {

    }

    public interface BeanHome extends javax.ejb.EJBHome {

      public ValidationErrors validate(BeanDetail detail);
    }

    public class Bean {

      public void ejbCreate(BeanDetail detail) {
        ValidationErrors errors = ejbHomeValidate(detail);
        if ( errors.size() > 0 )
          throw new CustomCreateException(errors);
        else
         //do the insert statement.
      }

      public void ejbStore() {
        ValidationErrors errors = ejbHomeValidate(internalDetail);
         if ( errors.size() > 0 )
          .
          .
          .

      }

      public ValidationErrors ejbHomeValidate(BeanDetail detail){
        ValidationErrors errors = new ValidationErrors();
        if ( detail.name == null )
          error.addIssue("name","Name is required");
      }
    }

    This is the basic structure.

    The trick here is that since the BeanHome.validate() method is stateless and available outside the bean the servlet or JSP level code can utilize it. In order to make mapping between the actual web form element and the detail object property, we have simply named each form element after the detail object property it is meant to alter. Ala:

    <input type="text" name="name" value="<%= detail.name %>">

    Now the target of the form can use the BeanHome.validate() to check a detail object before storing it:

    <%

    BeanHome home = lookupObject("BeanHome");
    BeanDetail detail = new BeanDetail();
    detail.name = request.getParameter("name");
    ValidationErrors errors = home.validate(detail);
    if ( errors.size() > 0 ) {
      request.setAttribute("ValidationErrors",errors);
      request.setAttribute("BeanDetail",detail);
    %>
    <jsp:forward page="webForm.jsp" />
    <%
    }
    %>

    The webForm can now inform the use what is wrong with the data they submitted.

    To make the mapping between the form elements and the detail object properties more solid, reflection could be used. I leave that as an exercise for the reader.
  2. I'm not really sure what advantage using the home method has over simply using a normal class with a validate method. Surely, using the home method from a client will result in an unnecessary network call, whereas a normal, locally loaded Validation class will work locally. I may have missed something though.
  3. The advantage is more figurative than literal. You would want to wrap up the valiation code with the object that it validates so it belongs inside the Bean.

    Also you may need to look up other data from the database in order to perform your validation and since we are using BMP anyway, we have easy access to a database connection. I feel more comfortable with that than I do making database aware Validation objects.
  4. The data validation process can further be generalized and separated by encaptulating it into another dedicated bean which would use some metadata about various detail objects in the system that it gets from the dababase. This would also centralize the system-wide data validation.
  5. Why not use a session bean?[ Go to top ]

    Isn't this sort of thing exactly what session beans are for? This seems like a bastardization of the home interface, which I always thought was only for getting access to EJBs. The session bean can call out to the entity anyway, if it needs to, but you may need to do validation across multiple entity beans. Whose home interface would have that validation code? Certainly you will have some client-side validation as well. So, you could end up with validation all over the place.

    Why not have client-side validator objects for your Entity Bean value objects, and then have server-side validator EJBs for that? Seems easier to remember and deal with.

  6. Why not use a session bean?[ Go to top ]

    Home interfaces used to be for getting to EJB's, but with the 2.0 spec there is support for Home methods which basically replace stateless session beans.

    If you are validating multiple beans then the task of validation of each bean should be assigned to the home method of that bean.. If there is then an issue with one home method kicking off the process, then you could wrap that in a separate class.

    Why you don't want one set of validator objects for the value objects and then have server-side validator EJB's is because you have to duplicate a lot of code.
  7. Why not use a session bean?[ Go to top ]

    I hate this pattern as a pattern. i.e. as something that sould be applied generally.

    You are validating everywhere, including store. So if the container calls store 5 minutes after the value is set then the validation errors are never displayed to anyone. it simply breaks.

    You are validating the same data multiple times - in the client, in the setter, in the store. Basically you need to add flags to show its been validated already so don't bother again.

    If you are validating against data base reference information then its dog slow. And again the pattern encourages the coder to ignore the details of the validation, not concerning themselves with the timing issues.

    You are ignoring value objects which are still valid within remote EJB's. Value objects have traditionally been used for validation. The advent of local interfaces means these objects are not as efficient as pass by param, but with MVC architectures like struts the value objects are still hugely valuable, although slanted more towards presentation than data model (probably).

    Basically I hate it. But thats OK, cos for your particular project it may make perfect sence. It's just not a pattern.

    Jonathan