Skip to main content

Implementing Validation Rules using Aspects

November 8, 2005

{cs.r.title}









Contents
Introduction
Data Validation Rules
Rules Engines
Data Validation: Cross-Cutting Concern?
Validation Framework
Sample Application
Technologies
Loan Application Use Case
Testing Validation Rules
Conclusion
Resources

Two years ago, I worked on a web portal project where we
implemented a custom validation framework to validate the data
entered by the user. I defined validation rules in XML files and
wrote Java code to parse and apply the rules to different data
fields on the web pages. One of the limitations of this solution
was that we had to add or modify code--on both client and server
tiers--whenever we added new fields on the screens that needed
data validation or when validation rules changed, per business
requirements. If I had to write the validation module again, I
would use aspects to dynamically weave validation logic into the
application code. With aspects, it would be more flexible to inject
validation into any existing code, whether it's on the client, to
verify if user entered data is valid, or on the server, where we
want to make sure the data is accurate before populating it into
database tables.

This article provides an overview of AOP-based data validation
implementation in a sample loan processing application. We will
look at validation rules for data fields with different validation
requirements, which is common in most real-world applications. We
will use annotations-based aspects (using AspectJ) to dynamically
weave validation rules into existing application code where the
data validation is required.

Introduction

Data validation (also known as primary validation or sanity
check
) is an essential requirement in any Java EE application.
Input data, whether it's entered by a user on a HTML form or as the
input parameters coming from an external web service client, needs
to be checked for valid data type, format, and the accuracy of the data
to ensure that it complies with data integrity requirements before
we actually process and store it in a back-end database. Failure to
detect invalid data could result in bad data getting into the
database.

In some applications, data validation may be a trivial task
because the data validation requirements are pretty straightforward
and writing a separate validation module is an overkill. But in
other enterprise applications (especially in financial, insurance,
or manufacturing companies), data validation is a vital part of the
application, and extensive validation rules are required to
validate the data transmitted across different tiers of the
application.

Security is another important consideration in data
validation. With new regulations like the Sarbanes Oxley Act (SOX),
validating user input has become one of the most important elements
of application design. Security vulnerabilities such as cross-site
scripting, phishing, and SQL injection could lead to major security
attacks on corporate websites. Stephen Enright wrote a two-part
series of articles ( "http://today.java.net/pub/a/today/2005/09/08/handling-java-web-app-input.html">
part one
, "http://today.java.net/pub/a/today/2005/09/20/handling-web-app-input.html">
part two
) on the importance of validating user input and best
practices on how to handle the data validation.

Data validation logic is usually hard-coded in the application
code, tightly coupled with core business logic. This design doesn't
give us much flexibility to implement any validation rule changes
that occur due to the dynamic nature of the business. And if there are any
new data fields that need the same validation logic as one of the
existing fields, we have the same problem. We have to write code to
add validation logic to each new field. In most Java EE
applications, we need to check data validity both in the web layer
(servlet container) and the business logic layer (application
server).

We can address all of these problems by defining validation rules
in an XML file (or even better, by using a rules engine) and dynamically
inject the rules (using AOP) into those parts of application code
where validation is required. This approach gives us a
loosely coupled and extremely flexible validation framework.

There are different ways of implementing data validation on the
web client and on the server. JavaScript is the most common script
language used on the client, and Struts Validator, Spring Validator,
or some kind of custom validation framework is used in the MVC and
service layers. The following table summarizes these data
validation techniques with their usages and limitations.

Validation Framework Tier Usage Limitation
"http://en.wikipedia.org/wiki/JavaScript">JavaScript Web client (browser) Detect invalid data on the client before passing
it to server.
Limited to data validation in the browser.
JavaScript code is not reusable in other layers of the application.
Also, the web user can disable JavaScript execution in the browser,
meaning the validation code will not run.
"http://static.springframework.org/spring/docs/1.2.x/reference/validation.html#validator">
Spring Validator
MVC (servlet container) Used in the controller class to check the validity of data
submitted by the client before sending it to the service or DAO layers.
This way, invalid data is detected in the MVC layer rather than passing
it all the way to application or database servers.
Limited to MVC layer. We can't use validation
rules defined in the XML file in other layers of the application.
It's also limited to basic validation; we can't really use it to
define business-rules-based validations.
"http://jakarta.apache.org/commons/validator/">Commons
Validator
MVC/service This framework provides a configurable validation
engine and reusable methods on primitive data validations.
We can't really use this framework as a robust
business rules validation tool.
Custom Validation Framework MVC/service/DAO A customized solution, so we can design it to address
specific validation requirements in enterprise applications.
Coding, testing, and maintenance overhead. May not
be as extensive and flexible as the available validation
frameworks.

Data Validation Rules

Typically, data validation requirements have the following
characteristics.

  1. Validation rules should be simple and atomic.
  2. Validation rules range from basic checks such as data type
    (integer or string) and format (SSN or date), to rules that use
    sophisticated business logic to verify if submitted data is
    valid.
  3. We need to perform data validation on the client so we don't
    send any invalid data all the way to server, realize it's not valid,
    and send it all the way back to the client. The users may have to
    wait longer before they are told the entered data is invalid, and
    they have to re-enter or modify the data. It could also add
    significant overhead on network resources to send data between the web
    client and application server. Client validations usually include
    basic checks such as checking data type, format, and so on.
  4. We need to perform similar validation checks on the server to
    make sure submitted data is good enough to be populated in the
    back-end database. We need server-side validation if there are
    multiple client applications, accessing the same server code, that are
    written in different programming languages such as a servlet or programs in Java
    Swing, PowerBuilder, or Visual Basic. Server validations are
    usually more sophisticated than client-side checks and include
    checking for some business rules in addition to basic data type
    checks.

Nowadays, rules engines are gaining more popularity in
encapsulating business and validation rules in enterprise
applications to reduce the cost of managing the rules. Let's
briefly take a look at the advantages of using a rules engine to
capture business rules.

Rules Engines

Rules engines collect complex decision-making logic and work
with a group of rule sets, usually defined in XML files, to make
decisions on what action needs occur when a specific condition is
true. The main driving factor behind using a rules engine for
managing rules is that business rules change very often and
customers (end users) are always demanding that these changes be
rolled out into a production environment as soon as possible to keep up with competitors. Managing business logic using a rules
engine gives us the flexibility we need to keep up with the dynamic
nature of business process.

Following are some of the rules engine frameworks (both open
source and commercial) that are currently available:

Data Validation: Cross-Cutting Concern?

In most Java enterprise applications, we have to implement
client and server validations. For these validations, we could use
different implementations on the client and server. For example, we
could use Struts Validator (or Spring Validator) in the MVC layer and a
custom validation framework on the server. But then we would have
to change code in two places whenever validation rules change for a
specific data field, as business requirements change from time to
time.

Data validation cuts across different application layers and
can be treated as a cross-cutting concern, just like other
cross-cutting concerns like persistence, security, exception
handling, and object caching. It makes a lot of sense to use
aspects to add validation logic into existing application code.

Before we get into the details of a validation framework and its
implementation using rules engine and aspects, let's compare the
traditional way of implementing data validation in a Java EE
application to an aspect-oriented data validation. The following
table lists the steps involved in implementing data validation in a
typical application.

 #  Task Application Layer Traditional Aspect-Oriented
1 Client submits the form filled in with data. Check
the data to make sure data format and values are acceptable.
Web client Use JavaScript to process data validation in the
browser.
Define validation rules using XML and a rules
engine.
2 Validate data submitted by client for data
accuracy.
MVC layer Use Spring Validator or similar framework to
perform data validation.
Write validation code outside of the actual
application code.
3 Check the data again to make sure it's valid and
passes business validation rules.
Service layer Design and implement validation logic in the
server code.
Define aspects to inject validation logic into the
application code (MVC or service layers).
4 Flexibility All layers Validation code is tightly coupled with
application code. Changes in validation code impacts the
application code, and vice versa.
Loosely coupled and flexible, so validation and
application code can change independently.

Validation Framework

I created a sample application called LoanProc to use the
proposed AOP Data Validation framework. This application is a home
loan processing application in a mortgage lending company. The LoanProc
application source is in the sample code (see "#resources">Resources below). Let's take a look at some of the
objectives we want to accomplish in the AOP Validation
framework.

  • Validation rules should be easy to define and maintain without
    having to modify any Java code.
  • The validation layer should be a centralized point for all
    validations in the application. Data validation should occur in any
    application tier without duplicating validation code in each
    tier.
  • Validation code should be written and maintained declaratively
    rather than programmatically.
  • Validation code should be flexible so it will be easier to
    modify the logic whenever business requirements change.
  • The framework should give us the ability to apply validation
    rules on new data fields without having to write the same logic
    each time a new field is added on a web page or a server
    module.
  • The validation layer should be completely separate from the other
    application layers.

Sample Application

To keep it simple for demonstration purposes, our sample loan
application page includes four data fields to show how validation
rules can be applied to fields with various data types and formats.
These validations include basic checks on date of birth and SSN
fields, and server validations on loan amount and loan to value
(LTV) fields. Following table shows the details of these
validations.

Field Name Data Type Client Validation Rules Server Validation Rules
Date of Birth Date Valid date None
SSN Number Data must be a number, nine digits in length None
Loan Amount BigDecimal Must be a number Value cannot be more than 1,000,000
LTV Number Must be a number. Value must be less than 1.0 LTV cannot be more than 0.85. (This means that we
don't want to offer a loan to those who are not putting down at
least 15 percent of the loan amount as down payment.)

It's a good practice to define the validation rules for each
module or web page in a separate rules XML file. I defined loan
application validation rules in a text file called
LoanApplicationRules.xml.

Technologies

I used different open source technologies and frameworks in the
sample application. I chose the Drools rules engine framework to define
validation rules. Drools is getting more attention in capturing
business rules in Java enterprise applications. Check out the
article " "http://www.onjava.com/pub/a/onjava/2005/08/03/drools.html">Give
Your Business Logic a Framework with Drools
" for more
information on using Drools for implementing business logic. I also
used the Spring and Hibernate frameworks in service and data access
layers, respectively, as these frameworks provide loosely coupled,
flexible, and extensible solutions when working on business logic
and object-relational modeling tasks. I used AspectJ (version 5.0)
to define validation aspects and weave them into controller and
service layers of the application. The table below lists these
technologies by the application tier.

Tier Technology/Framework
Client JUnit, HttpClient
MVC Spring MVC
Service Spring Framework
DataAccess DAO
Rules Drools 2.0
AOP AspectJ, AJDT 1.3
Application Server JBoss 4.0
Database Hypersonic SQL
IDE Eclipse 3.1

Note: I used AJDT 1.3 for the latest Eclipse version
(3.1). This is not a stable release. If you want to test the sample
application using a stable AJDT release, use AJDT 1.2 for Eclipse
3.0. Also make sure the Eclipse project is set up with J2SE 5.0,
since the application uses annotations to define aspects.







Loan Application Use Case

The use case for the loan processing application starts with the
user entering loan details in an HTML form and submitting it, which
triggers data validation on both the client and server tiers.
Depending on successful data validation, a confirmation number is
provided to the user that can be used for future inquiries on the
loan application. These steps are shown in the sequence diagram in
Figure 1.

Figure 1
Figure 1. Loan Processing application data flow (Click on the
screen shot to open a full-size view.)

Now, let's look at the main components of the sample
application.

  1. Client interface: I used Spring MVC for the controller
    (web navigation) layer in the loan processing application. This
    includes a JSP as the view and a Spring Controller to handle the
    navigation.
  2. The listing below shows the sample code of the
    onSubmit method controller class
    (LoanApplicationController).

    
    protected ModelAndView onSubmit(HttpServletRequest request,
            HttpServletResponse response,
            Object command, BindException errors)
            throws Exception {
    
        System.out.println("Enter onSubmit().");
        LoanApplication loanApp = (LoanApplication)command;
        System.out.println("LoanApplication:\n"+loanApp.toString());
    
        int confirmationNumber = service.registerLoanApplication(loanApp);
    
        // Get opportunities
        request.setAttribute("confirmationNumber",
            Integer.toString(confirmationNumber));
    
        return new ModelAndView(getSuccessView());
    }
    
  3. Service layer: I used Spring for writing the service
    layer that calls the DAO object to perform CRUD operations on the
    LoanApplication table in the back-end database.
  4. A code snippet of the registerLoanApplication
    method in the LoanApplicationService class is shown
    below.

    
    public int registerLoanApplication(LoanApplication loanApp)
    {
        int confirmationNumber = 0;
    
        try
        {
            dao.updateLoanApplication(loanApp);
    
            // Get confirmation number for the submitted loan application
            confirmationNumber =
                dao.getLoanApplication(
                    loanApp.getLoanAppId()).getConfirmationNumber();
    
    
        } catch (DAOException e)
        {
            log.error(e,e);
        }
        return confirmationNumber;
    }
    
  5. Database: I created the loandb database using
    the Hypersonic SQL database. The database has a single table called
    LoanApplication.
  6. Rules: I defined validation rules in an XML file and
    used the Drools API to load and apply these rules to the data
    fields.
  7. The validation rules for the loan application fields are defined
    in rules XML files as shown below.

    
    <?xml version="1.0"?>
    <rule-set name="LoanApplicationRules"
            xmlns="http://drools.org/rules"
            xmlns:java="http://drools.org/semantics/java"
            xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
            xs:schemaLocation="http://drools.org/rules rules.xsd
            http://drools.org/semantics/java java.xsd">
    
        <!-- Java Imports -->
        <java:import>java.lang.Object</java:import>
        <java:import>java.lang.String</java:import>
        <java:import>com.loanapp.domain.LoanApplication</java:import>
    
        <!-- Java function to print message -->
        <java:functions>
            public void printMessage(
                com.loanapp.domain.LoanApplication loanApp,
                String msg)
            {
                System.out.println(msg);
            }
        </java:functions>
    
        <!-- Validate SSN -->
        <rule name="SSN Validation">
    
            <parameter identifier="loanApplication">
                <class>LoanApplication</class>
            </parameter>
    
            <java:condition>
                loanApplication.getSsn().length() < 9
            </java:condition>
    
            <java:consequence>
                printMessage(loanApplication,
                    "Invalid SSN - should be 9 digits.");
            </java:consequence>
        </rule>
    
        <!-- Check Loan Amount -->
        <rule name="LoanAmount Is High">
    
            <!-- Parameters -->
            <parameter identifier="loanApplication">
                <class>LoanApplication</class>
            </parameter>
    
            <!-- Validate the data field -->
            <java:condition>
                loanApplication.getLoanAmount().intValue() > 1000000
            </java:condition>
    
            <java:consequence>
                printMessage(loanApplication,
                    "LoanAmount is higher than the allowed limit.");
            </java:consequence>
        </rule>
    
    </rule-set>
    

    And the code implementation of loading these validation rules
    using Drools API is as follows:

    
    public void doValidation(BaseDomainObject domainObj)
        throws ValidationException
    {
        runValidationRules(domainObj);
    }
    
    public void runValidationRules(BaseDomainObject domainObj)
    {
        try
        {
            // Validation Rules File Name
            String ruleFileName = 
                domainObj.getClass().getName()+"Rules.xml";
            // Load the validation rules
            loadRules(ruleFileName);
    
            WorkingMemory workingMemory =
                validationRules.newWorkingMemory();
            workingMemory.addEventListener(
                new DebugWorkingMemoryEventListener());
            workingMemory.assertObject(domainObj);
    
            // Fire rules
            workingMemory.fireAllRules( );
        }
        catch (Exception e)
        {
        }
    }
    
    /**
     * Load the business rules from the xml file
     */
    private static void loadRules(String ruleFileName)
        throws Exception
    {
        if (validationRules == null)
        {
            //Specify this resolver when we load are rules
            validationRules = RuleBaseLoader.loadFromUrl(
                    BusinessLayer.class.getResource(
                        "/rules/"+ruleFileName));
        }
    }
    
  8. Aspects: I wrote aspects to apply validation rules to
    various data fields (or methods, in the case of server code) in the
    loan application. We inject these rules into the controller (MVC layer)
    and service classes to implement the validation rules wherever we
    need them. For more information on Aspects and AOP check, out the
    articles " "http://www.onjava.com/pub/a/onjava/2004/01/14/aop.html">Introduction
    to Aspect-Oriented Programming
    " and " "http://www.onjava.com/pub/a/onjava/2004/05/12/aop.html">Performance
    Analysis of J2EE Applications Using AOP Techniques
    ."
  9. The following listings show the pointcuts and advices defined to
    inject validation code into the LoanApplicationController
    and LoanApplicationService classes, respectively.

    Here's a pointcut and around advice to intercept the
    onSubmit() method and apply validation on the LoanApplication object.

    
    @Pointcut(
           "execution(* *.onSubmit(..)) && args(request, response, command, errors)"
    )
    void executeOnSubmit(HttpServletRequest request,
           HttpServletResponse response,
           Object command, BindException errors) {};
    
    @Before("executeOnSubmit(request, response, command, errors)")
    public void beforeOnSubmit(HttpServletRequest request,
           HttpServletResponse response,
           Object command, BindException errors) {
       System.out.println("beforeOnSubmit");
    }
    
    @Around("executeOnSubmit(request, response, command, errors)")
    public Object aroundOnSubmit(ProceedingJoinPoint jp,
           HttpServletRequest request,
           HttpServletResponse response,
           Object command, BindException errors) {
       Object proceedResult = null;
       System.out.println("aroundOnSubmit");
       try {
           DataValidator validator = new DataValidator();
           BaseDomainObject domainObj = (BaseDomainObject)command;
           validator.doValidation(domainObj);
    
           proceedResult = jp.proceed();
       } catch (Throwable t) {
           log.error(t,t);
       }
       return proceedResult;
    }
    
    

    Here's the pointcut and advice for the
    registerLoanApplication() method in
    LoanApplicationService class.

    
    @Pointcut(
          "execution(* *.registerLoanApplication(..)) && args(loanApp)"
    )
    void executeRegisterLoanApplication(LoanApplication loanApp) {};
    
    @Before("executeRegisterLoanApplication(loanApp)")
    public void beforeRegisterLoanApplication (
            LoanApplication loanApp) {
       System.out.println("before");
    }
    
    @Around("executeRegisterLoanApplication(loanApp)")
    public int aroundRegisterLoanApplication (
            JoinPoint thisJoinPoint, LoanApplication loanApp) {
       System.out.println("aroundRegisterLoanApplication");
       return 0;
    }
    

Figure 2 shows the different elements of the validation framework
and their relationship with each other.

Figure 2
Figure 2. Application setup diagram (Click on the screen shot to
open a full-size view.)

Testing Validation Rules

I wrote two JUnit test scripts (DataValidatorTest
and LoanApplicationControllerTest) to verify that
validation is working correctly by submitting both valid and
invalid data. If validation fails, we set the
validation flag in the LoanApplication object
to false, indicating that the data cannot be sent to
the server. LoanApplicationControllerTest is a good
example, since it can be executed outside the J2EE container and the
DAO returns a test confirmation number. I also wrote a test client
(called LoanApplicationClient) using Commons
HttpClient to simulate a web request with data fields filled in
with the loan application details. Once the Submit button is
clicked, validation rules are applied to the submitted data and
validation errors are returned for any invalid data.

If basic validation passes, server-side validation rules are
then applied on the data sent by the client. If there is a violation of
any of the rules, server will return error messages for the invalid
data.

Conclusion

In this article, we looked at the implementation of data
validation rules using a rules engine and aspects. Data validation
is an essential part of any enterprise application, and using
aspects to implement it offers the flexibility to respond in an
agile manner to any changes in validation requirements.

We defined data validation rules using a rules engine (Drools) and
used aspects to weave validation logic in those classes where
validation is required to verify the quality of user-entered
data.

Since we need some kind of validation on the web client so that we
don't send any invalid data past the client layer, a better
solution is to use JavaScript for basic validation (such as
checking empty or null values and format) and process all other types
of validations (such as checking for value and business
validations) using a centralized custom data validation
framework.

Sometimes, validation rules are stored in a SQL database. It's a
good idea to store the validation rules in memory (cache) after
loading them from the database for the first time. This way, we
won't be hitting the database every time we need to validate a data
field.

An administration tool to add new validation rules or modify or
delete existing rules (preferably a JMX-based solution) would be a
nice enhancement to the proposed validation framework.

Note that both object caching and monitoring are also
cross-cutting concerns that can use the help of aspects for their
implementation. Check out "http://www.theserverside.com/articles/article.tss?l=ObjectCachingWithAOP">
this article
on how we can implement object caching using AOP
techniques.

Resources

width="1" height="1" border="0" alt=" " />
Srini Penchikala presently works as an enterprise architect at Flagstar Bank.
Related Topics >> Programming   |   

Comments

Iam trying to implement

Iam trying to implement something similar..Can you share a sample example code in .zip format which can be helpful.. thnx in advance