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 (
part one ,
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 |
| 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. |
|
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. |
| 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.
- Validation rules should be simple and atomic.
- 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.
- 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.
- 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 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 "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. 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.
- 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.
-
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());
}
- 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.
-
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;
}
- Database: I created the
loandb database using
the Hypersonic SQL database. The database has a single table called
LoanApplication.
- Rules: I defined validation rules in an XML file and
used the Drools API to load and apply these rules to the data
fields.
-
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));
}
}
- 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 "Introduction
to Aspect-Oriented Programming " and "Performance
Analysis of J2EE Applications Using AOP Techniques ."
-
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. 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
this article on how we can implement object caching using AOP
techniques.
Resources