Archive for August, 2009

22
Aug
09

Car Rental Web and Annotations with Commons Validator

In this article we will configure a Spring MVC project that uses Annotations with the popular Commons Validator Framework.

Please ensure you have done the following before continuing with this article.

Requirements

After you have the car rental application functioning you can follow these simple steps to get your form validated with commons validator.

Ensure you have the following keys defined in the resource property file.

Insert the following into the resource bundle property file. If the values are not there then add them.

info.firstName=First Name
info.lastName=Last Name

My validation.xml file looks like this…

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE form-validation PUBLIC
 "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.1.3//EN"
 "http://jakarta.apache.org/commons/dtds/validator_1_1_3.dtd">

<form-validation>

 <formset>
 <form name="carReservation">
 <field property="party.firstName" depends="required">
 <arg key="info.firstName" />
 </field>
 <field property="party.lastName" depends="required">
 <arg key="info.lastName" />
 </field>
 </form>
 </formset>

</form-validation>

All that needs to be done is to specify the name of the form and the fields that need validation.

For each controller you need to inject the beanValidator so that it is available for the method that will handle the post.

There are 2 ways to get this done.

  1. Define your bean in a spring configuration file
  2. Use the @Autowired functionality

Option 1 kind of defeat’s the purpose of using Annotation based controllers. The whole point was not to have your controller defined in a spring configuration file.

Option 2 is better and that is what we will do here. Put the following into your Controller:

	@Autowired
	@Qualifier("beanValidator")
	private Validator beanValidator;	

We don’t need getters or setters for beanValidator, apparently spring takes care of setting the values internally.

Put the following into the begining of the method that handles the POST.

		beanValidator.validate(carReservation, result);
		if(result.hasErrors()) {
			return "carNew";
		}

My NewRequestAnnotationController looks like this:

package test;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.validation.BindingResult;
import org.springframework.validation.Validator;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.support.SessionStatus;

@Controller
@RequestMapping("/newRequestAnnotation")
public class NewRequestAnnotationController {

	@Autowired
	@Qualifier("beanValidator")
	private Validator beanValidator;	

	@RequestMapping(method = RequestMethod.GET)
	public String processGET(ModelMap model) {
		CarReservation carReservation = new CarReservation();
		carReservation.getParty().setFirstName("John");
		carReservation.getParty().setLastName("Doe");
		model.addAttribute("carReservation", carReservation);		
		return "carNew";
	}
	
	@RequestMapping(method = RequestMethod.POST)
	public String processPost(
			@ModelAttribute("carReservation") CarReservation carReservation,
			BindingResult result, SessionStatus status) {
		
		beanValidator.validate(carReservation, result);
		if(result.hasErrors()) {
			return "carNew";
		}
				
		System.out.println("processing post request.");
		System.out.println("FirstName:"
				+ carReservation.getParty().getFirstName());
		System.out.println("LastName:"
				+ carReservation.getParty().getLastName());

		return "selectDate";
	}	
}

Save and restart…

You can navigate to your application by typing

http://localhost:8080/carRentalWeb/app/newRequestAnnotation

That’s All!!!

14
Aug
09

Default Log4j Property File

This page describes the process of setting up a Log4j configuration file. It goes thru some example log4j configuration files and describes what each one does. I finish up by discussing TimeBasedRollingPolicy with an example.

Requirements

  • Basic Understanding of Maven

POM

The following dependencies should be placed in your pom.xml file before starting.

<dependency>
	<groupId>log4j</groupId>
	<artifactId>log4j</artifactId>
	<version>1.2.15</version>
	<exclusions>
		<exclusion>
			<groupId>javax.mail</groupId>
			<artifactId>mail</artifactId>
		</exclusion>
		<exclusion>
			<groupId>javax.jms</groupId>
			<artifactId>jms</artifactId>
		</exclusion>
		<exclusion>
			<groupId>com.sun.jdmk</groupId>
			<artifactId>jmxtools</artifactId>
		</exclusion>
		<exclusion>
			<groupId>com.sun.jmx</groupId>
			<artifactId>jmxri</artifactId>
		</exclusion>
		<exclusion>
			<groupId>javax.activation</groupId>
			<artifactId>activation</artifactId>
		</exclusion>
	</exclusions>
</dependency>
	<dependency>
	  <groupId>commons-logging</groupId>
	  <artifactId>commons-logging</artifactId>
	  <version>1.0.4</version>
	</dependency>
    <dependency>
      <groupId>log4j</groupId>
      <artifactId>apache-log4j-extras</artifactId>
      <version>1.0</version>
    </dependency>

This is the default log4j.properties file with basic logging to the console. Be careful with it because it will be verbose if you are running in a servlet engine or JEE container. a more reasonable value for the rootLogger would be FATAL. Additional packages can be defined on a case by case basis.

Property Based Configuration (Legacy)

# Set root logger level to DEBUG and its only appender to A1.
log4j.rootLogger=DEBUG, A1

log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=%d %-5p %c - %m%n

Additional packages could be added to to the file by putting in the following lines:

log4j.logger.org.vtechfw=DEBUG
log4j.logger.org.vtechfw.web=INFO

XML Based Configuration

A similar configuration however in XML is seen below.




<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
	<appender name="A1" class="org.apache.log4j.ConsoleAppender">
		<param name="Target" value="System.out">
		<layout class="org.apache.log4j.PatternLayout">
			<param name="ConversionPattern" value="%d %-5p %c - %m%n">
		</layout>
	</appender>

	<logger name="org.vtechfw">
		<level value="debug"></level>
	</logger>
	<root>
		<priority value="fatal"></priority>
		<appender-ref ref="A1"></appender-ref>
	</root>

</log4j:configuration>

RollingFileAppender Example

The following appender will roll when the file size is reached.

  <appender name="R" class="org.apache.log4j.RollingFileAppender">
    <param name="file" value="example.log">
    <param name="MaxFileSize" value="100KB">
    <!-- Keep one backup file -->
    <param name="MaxBackupIndex" value="1">
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="%p %t %c - %m%n">
    </layout>
  </appender>

DailyRollingFileAppender Example

The following appender will log to the /tmp/test.log file and at midnight it will roll it over to a dated filename and continue with a fresh test.log file for the next day.

	<appender name="DRFA" class="org.apache.log4j.DailyRollingFileAppender">
		<param name="File" value="/tmp/test.log">
		<param name="DatePattern" value="'.'yyyy-MM-dd">
		<param name="Append" value="true">
		<param name="Threshold" value="DEBUG">
		<layout class="org.apache.log4j.PatternLayout">
			<param name="ConversionPattern" value="%d %-5p %c - %m%n">
		</layout>
	</appender>

TimeBasedRollingPolicy Example

This Policy is the most flexable way to roll your files over. You can not only customize how often the file rolls over but the policy will allow you to compress your log files using .zip or .gz files.

This Rolling Policy was was originally intended to be in the next version of log4j but the development effort for that stopped. Therefore the log4j team decided to roll it up into an “extra” package. In order to use the log4j-extra package you need to make sure to include the following in your pom.xml file

    <dependency>
      <groupId>log4j</groupId>
      <artifactId>apache-log4j-extras</artifactId>
      <version>1.0</version>
    </dependency>

This is what my log4j.xml looks like.




<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">
	<appender name="A1" class="org.apache.log4j.ConsoleAppender">
		<param name="Target" value="System.out">
		<layout class="org.apache.log4j.PatternLayout">
			<param name="ConversionPattern" value="%d %-5p %c - %m%n">
		</layout>
	</appender>

    <appender name="DRFA" class="org.apache.log4j.rolling.RollingFileAppender">
		<rollingpolicy class="org.apache.log4j.rolling.TimeBasedRollingPolicy">
		    <param name="FileNamePattern" value="/tmp/info-%d{yyyy-MM-dd-HH-mm}.log.gz">
		    <param name="ActiveFileName" value="/tmp/info.log">
		</rollingpolicy>
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d %-5p %c - %m%n">
        </layout>
    </appender>
	<logger name="com.test">
		<level value="debug"></level>
		<appender-ref ref="DRFA"></appender-ref>
	</logger>
	<root>
		<priority value="fatal"></priority>
		<appender-ref ref="A1"></appender-ref>
	</root>
</log4j:configuration>

  1. If you put a “.gz” or “.zip” at the end of your FileNamePattern then the Policy will compress after rolling over.
  2. The FileNamePattern also dictates how often the roll over occurs.
    • %d – daily
    • %d{yyyy-MM} – monthly
    • %d{yyyy-MM-dd} – daily
    • so on and so forth

To use the appender just include line highlighted below in your logger element:

	<logger name="org.vtechfw">
		<level value="debug"></level>
		<appender-ref ref="DRFA"></appender-ref>
	</logger>

In the Java Code

In the above example classes in the org.vtechfw package would print DEBUG messages and above and in the web package INFO messages and above.

Logging In your Source Code

    protected final Log logger = LogFactory.getLog(getClass());

After you define the above attribute just use one of the convenience methods to log.

Fastest Way of Logging

For some Logger logger, writing,

logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));

incurs the cost of constructing the message parameter, that is converting both integer i and entry[i] to a String, and concatenating intermediate strings. This, regardless of whether the message will be logged or not.

One possible way to avoid the cost of parameter construction is by surrounding the log statement with a test. Here is an example.

if(logger.isDebugEnabled()) {
logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
}

The jury is out on wheather to do this across the board or only with log statements that have string concatination. Its your code. You decide.

Side note:
TOO much logging? Consider implementing a batch logger. It would dump a log to the file only when there is some type of error.

13
Aug
09

Creating a Web Server Certificate

Creating a certificate is a 2 step process

  • Generate a certificate Request
  • Sign a request with a CA’s signature

Requirements

Creating a private key and Certificate Request

openssl req -new -keyout pw-protected-privkey.pem -out my-server.csr -days 365

# provide the hostname of the server as the Common Name when creating the certificate.

pw-protected-privkey.pem : private key
newreq.pem : CSR

Remove the password from the server’s private key (unless you want the server to ask you each time)

openssl rsa -in pw-protected-privkey.pem -out my-server-private-key.pem

sign the csr using your own CA

You now have to send this Certificate Signing Request (CSR) to a
Certifying Authority (CA) for signing. The result is then a real
Certificate which can be used for Apache.

Or you can sign it using your own CA

openssl ca -verbose -in my-server.csr -out my-server.cert -keyfile demoCA/private/cakey.pem -cert demoCA/cacert.pem

# during signing you should see the following output

Certificate is to be certified until Apr 5 03:09:42 2010 GMT (365 days)
Sign the certificate? [y/n]:y
1 out of 1 certificate requests certified, commit? [y/n]y
Write out database with 1 new entries
writing new certificates
writing ./demoCA/newcerts/01.pem
Data Base Updated

08
Aug
09

Car Rental Web Application

This article will show you how to modify the hello world application to accept user form input.

The car rental web application allows the user to enter some basic information in a form. The form gets validated once the user submits.

Requirements:

Please make sure you have went thru the instructions necessary to get a Spring MVC hello world application up and running. Once you have verified that it works continue below.

The following is a declaration for a controller

spring-servlet.xml

	<bean name="/newRequest" class="test.NewRequestFormController">
<property name="commandName" value="address"/>
<property name="commandClass" value="test.Address"/>
<property name="formView" value="carNew"/>
<property name="successView" value="selectDate"/>
	</bean>

The following is the sourcecode for the NewRequestFormController

package test;

import java.text.SimpleDateFormat;
import java.util.Date;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.web.bind.ServletRequestDataBinder;
import org.springframework.web.servlet.mvc.SimpleFormController;

public class NewRequestFormController extends SimpleFormController {
	private CustomerModel customerModel;

	public CustomerModel getCustomerModel() {
		return customerModel;
	}

	public void setCustomerModel(CustomerModel customerModel) {
		this.customerModel = customerModel;
	}

	@Override
	protected void doSubmitAction(Object command) throws Exception {
		CarReservation carReservation = (CarReservation)command;

		Address address = carReservation.getAddress();
		Party party = carReservation.getParty();

		System.out.println("Processing the address object: " + address.getName());
		System.out.println("Date successfully processed: " + address.getToday());
		customerModel.reserveCar(carReservation);

	}

	@Override
	protected void initBinder(HttpServletRequest request,
			ServletRequestDataBinder binder) throws Exception {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        dateFormat.setLenient(false);
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
	}
}

Next we will modify /WEB-INF/carNew.jsp

<?xml version="1.0" encoding="ISO-8859-1" ?>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib uri="http://www.springmodules.org/tags/commons-validator" prefix="validator" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>XHTML 1.0 Transitional Document</title>
<meta http-equiv="Content-type" content="text/html; charset=iso-8859-1" />
<meta http-equiv="Content-Language" content="en-us" />

</head>

<body>
<div id="header" align="center">Car Rental Request App</div>
<div id="nav">
<ul>
	<li><a href="#">New Rental Request</a></li>
</ul>
</div>
<div id="content">
<h2>Step 1 | Enter Car pickup Address</h2>
<div id="formcontainer">
  	
Please enter your information here: 
  	<form:form commandName="address" name="address" method="post">
<div class="clearfix">
	  		<label>Name</label>
	  		<form:input path="name"/>
	  		<form:errors path="name" cssClass="error"/></div>
<div class="clearfix">
	  		<label>street</label>
	  		<form:input path="street"/>
	  		<form:errors path="street" cssClass="error"/></div>
<div class="clearfix">
	  		<label>city</label>
	  		<form:input path="city"/>
	  		<form:errors path="city" cssClass="error"/></div>
<div class="clearfix">
	  		<label>state</label>
	  		<form:input path="state"/>
	  		<form:errors path="state" cssClass="error"/></div>
<div class="clearfix">
	  		<label>zip</label>
	  		<form:input path="zip"/>
	  		<form:errors path="zip" cssClass="error"/></div>
<div class="clearfix">
	  		<label>today</label>
	  		<form:input path="today"/> yyyy-MM-dd
	  		<form:errors path="today" cssClass="error"/></div>
<div class="clearfix" align="right">
			<input type="submit"/></div>
</form:form></div>
</div>
</body>
</html>

The following is the success page. It allow the user to continue the interaction.
/WEB-INF/jsp/selectDate.jsp

<?xml version="1.0" encoding="ISO-8859-1" ?>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
<title>XHTML 1.0 Transitional Document</title>
<meta http-equiv="Content-type" content="text/html; charset=iso-8859-1" />
<meta http-equiv="Content-Language" content="en-us" />
	<link href="<c:url value='/css/default.css'/>" rel="stylesheet" type="text/css"/>
</head>

<body>
<div id="header" align="center">Car Rental Request App</div>
<div id="nav">
<ul>
	<li><a href="#">New Rental Request</a></li>
</ul>
</div>
<div id="content">
<h2>Step 2 | Enter Car pickup Date</h2>
</div>
</body>
</html>

Navigate to the following url and you should see the input form.

[yourcontextroot]/app/newRequest;

Submit the form and you should be presented with the next page.

At this point you can continue the tutorial and learn about how to add form validation:
http://numberformat.wordpress.com/2009/08/01/spring-mvc-commons-validator-client-side-validation

07
Aug
09

Web Application Resource Bundles

Resource bundles allow web application programmers to internationalize their applications. It provides a single place for application developers to put text that will be displayed to the user.

This page talks about different types of web applications and how they manage their resource bundles. On this page we will cover the struts framework as well as Spring MVC.

Spring MVC

In order to get Message Property Resource Bundles to get registered in Spring you need to put the following in your spring configuration file.

 <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">  
     <property name="basenames">  
         <value>messages</value>  
     </property>  
 </bean> 

This code will look for messages.properties in your javasource folder. It will load the key value pairs and when the system needs to translate a key it will look into this file.

tag will translate all error messages using this property file.

06
Aug
09

DTC p1441 evap system vacuum during non purge

This code indicates that a vacuum was detected while the system was in the non purge state. Initially I did not know much about this and started reading some posts on the internet. All I knew is that my check engine light did not go out.

When the car is parked the fumes are actually stored in the vapor canister. This is made this way to prevent them from escaping into the atmosphere where they cause pollution. When the car is started the fumes are fed into the intake manafold where they are eventually burned. The car opens a valve that “purges” the canister by introducing a vacuum and sucking out the air from the canister. The vacuum is initiated by a “purge solenoid valve”. When 12v is applied to the terminals of this valve by the car’s computer the valve opens up and allows the air to escape into the intake manifold. A lot of times this valve goes bad and allows air to pass into the intake manafold all the time. The car also has a vacuum monitoring switch mounted close to the valve. The job of this switch is to monitor for a vacuum. When air is passing by the valve all the time the monitoring switch will detect a vacuum and alert the computer. The computer will issue a DTC p1441 code.

click here to view a good video about this
here is another older one

I have a 1996 Pontiac Firebird LT1.

I started replacing my EVAP purge solenoid and the vacuum monitoring switch, and Clearing the DTC code… No luck.

I had replaced my canister as well as the check valve connected to it. These parts are located on the left side quarter-panel at the back of the car behind the wheel. I also had the mechanic make sure the hose was clear of any charcoal by disconnecting it from everything and blowing air into it. I replaced part 11 and 7 seen in the diagram. The gas cap was also replaced. No luck… Now that I think about it replacing the GAS cap did not make any sense at all. The trouble code says that it is detecting a vacuum during a non-purge state. That means that there is a vaccume in the gas tank and that means the gas cap is NOT leaking.

I went crazy replacing anything and everything EVAP related in my car till I ran out of parts to replace. Went back TEST each part again and I found the defective valve.

Replaced the Purge solenoid valve for the 2nd time. And Problem SOLVED!!! It looks like the dealership send me a defective part or the part went bad soon after I got it. I am wondering what would cause the valve to go bad? Is compression breaking to blame??? I stopped doing that now. Hopefully I don’t have to see this dreadful code again.

To test the purge solenoid valve:
The purge solenoid valve should not permit flow when no voltage is applied to its terminal. As soon as 12v input is applied it should be free flowing. I used an instrument similar to this to test the system.

03
Aug
09

Excluding Dependencies in Maven

Maven has a very robust dependency management framework.

Requirements:

A transitive dependency is one where project A depends on Project B and project B depends on project C. In this case we call project C a transitive dependency of project A.

Maven manages transitive dependencies automatically. Take for example the following dependency in the pom.xml file.

		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.15</version>
		</dependency>

In the above example log4j dependency will automatically bring in the following dependencies into the build and runtime classpaths.

  1. jmxtools-1.2.1.jar
  2. jmxri-1.2.1.jar
  3. activation-1.1.jar
  4. jms-1.1.jar
  5. mail-1.4.jar

The activation-1.1.jar is actually not a direct dependency of this project. It is not even a direct dependency of log4j. It is actually a dependency of javamal (mail-1.4.jar). As you can see it was included in this project’s build classpath. Maven does not care about how deep the dependencies go it will go ahead and include them all.

project build classpath before dependency exclusions

Since all the above transitive dependencies are all runtime dependencies and are not required if you are simply logging to a file system you can provide the following statements into the pom.xml file.

To remove these un needed dependencies you need to put exclusions seen below. for activation-1.1.jar I needed to find out the dependency of mail-1.4.jar. I did this by going to the central maven repository and looking at the source code for mail-1.4.jar’s pom.xml file.

		<dependency>
			<groupId>log4j</groupId>
			<artifactId>log4j</artifactId>
			<version>1.2.15</version>
			<exclusions>
				<exclusion>
					<groupId>javax.mail</groupId>
					<artifactId>mail</artifactId>
				</exclusion>
				<exclusion>
					<groupId>javax.jms</groupId>
					<artifactId>jms</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.jdmk</groupId>
					<artifactId>jmxtools</artifactId>
				</exclusion>
				<exclusion>
					<groupId>com.sun.jmx</groupId>
					<artifactId>jmxri</artifactId>
				</exclusion>
				<exclusion>
					<groupId>javax.activation</groupId>
					<artifactId>activation</artifactId>
				</exclusion>
			</exclusions>
		</dependency>

After doing mvn eclipse:clean eclipse:eclipse then clicking refresh on the project, my build classpath looks like this…

build classpath after applying exclusions

02
Aug
09

Electrical Connector

Wiring harness that connect to the AIR injection pump went bad.

430_Series-150-3-way

Sealed Metri-Pack 150 Series Three-Way Housings and Locks

Female Connector Assembly
DESCRIPTION
A
OEM P/N
12110293

TPA Lock
B
OEM P/N
12052845

Male Connector Assembly
C
OEM P/N
12129615

430_MP150-2-Way-Group-435

WHITE PRODUCTS P/N
OEM P/N
DESCRIPTION
A
F150SS-2
12052641
Female Connector Assembly
B
150TPA-2
12052634
TPA Lock
C
M150SS-2
12162000
Male Connector Assembly

FEMALE TERMINALS
MPF-150S-18-21

MPM-150S-19-21

FEMALE TERMINALS
MPF-150S-18
(12048074)
20-16 Gauge
Tin-Plated


MPF-150S-21
(12084200)
22-20 Gauge
Tin-Plated
MALE TERMINALS
MPM-150S-19
(12045773)
20-16 Gauge
Tin-Plated


MPM-150S-21
(12077628)
22-20 Gauge
Tin-Plated

RWB-Seals
CABLE SEALS
MP-150S-BLU
(12048087)
Blue, 22 Gauge


MP-150S-RED
(12048086)
Red, 20-16 Gauge


MP-150S-WHT
(12089678)
White, 20 Gauge

01
Aug
09

Spring MVC Validation using Valang

Jakarta Commons Validation uses XML to declaratively specify constraints. The configuration is not intuitive and the configuration is too verbose for my tastes.

Valang is specifically designed for validating target objects and the syntax is short and to the point.

If you have not done so already please read the following link to get started.

In order to use valang you need to add the following into your Maven pom.xml

 		 <dependency>
			<groupId>org.springmodules</groupId>
			<artifactId>spring-modules-validation</artifactId>
			<version>0.8</version>
 		</dependency>

In spring-servlet.xml put the following:

<bean id="addressValidator" class="org.springmodules.validation.valang.ValangValidator">
  <property name="valang">
    <value>
    <![CDATA[
      { name : name HAS TEXT : 'Name is required'}
      { street : street HAS TEXT : 'Street is required' }
      { city : city HAS TEXT : 'City is required'}
      { state : state HAS TEXT : 'State is required' }
      { zip : zip HAS TEXT : 'Zip is required' }
    ]]>
    </value>
  </property>
</bean>

In the same file as above modify /newRequest bean and add the vaidator defined above.

	<bean name="/newRequest" class="test.NewRequestFormController">
		<property name="commandName" value="address"/>
	    <property name="commandClass" value="test.Address"/>
        <property name="formView" value="carNew"/>
		<property name="successView" value="selectDate"/>
		<property name="validator" ref="addressValidator"/>
	</bean>

Thats all!

Just restart the server and navigate to the page
[contextroot]//app/newRequest

Click submit without entering anything and you should start getting a whole mess of error messages.

The great thing about this is that if you are switching from commons-validator you dont need to modfy your jsp’s. The same “form:errors” tags that were used to define the form stay as is.

Client Side Validation

In order to get client side validation working you need to follow these simple steps.

First you need to make sure you defined the following handler mapping in your spring-servlet.xml file

<bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping">
    <property name="interceptors">
        <list>
            <bean class="org.springmodules.validation.valang.javascript.taglib.ValangRulesExportInterceptor" />
        </list>
    </property>
</bean>

Then you need to make sure you have the following defined up top of your JSP

<%@taglib uri="http://www.springmodules.org/tags/valang" prefix="valang" %>

In the head tag of your JSP make sure you have the following defined:

<valang:codebase includeScriptTags="true" fieldErrorsIdSuffix="_error" globalErrorsId="global_error"/>

Lastly.. you need to define the following from within the form tag:

<valang:validate commandName="address" />

At the time of this writing the 0.8a version of spring-modules-validation had an open bug with the client side validation using valang validation. It is described by the following url:
http://jira.springframework.org/browse/MOD-267

In order to fix this issue you need to apply the following patch that was submitted by Andrew May.

Index: projects/validation/src/java/org/springmodules/validation/valang/javascript/valang_codebase.js
===================================================================
RCS file: /cvs/springmodules/projects/validation/src/java/org/springmodules/validation/valang/javascript/valang_codebase.js,v
retrieving revision 1.7
diff -u -r1.7 valang_codebase.js
--- projects/validation/src/java/org/springmodules/validation/valang/javascript/valang_codebase.js	27 Oct 2006 02:00:11 -0000	1.7
+++ projects/validation/src/java/org/springmodules/validation/valang/javascript/valang_codebase.js	26 Oct 2007 15:03:36 -0000
@@ -119,21 +119,27 @@
         }
     },
     _findForm: function(name) {
+    	var formElement
         var element = document.getElementById(name)
-        if (!element || element.tagName.toLowerCase() != 'form') {
+		if (element && element.tagName.toLowerCase() == 'form') {
+			formElement = element;
+		}
+		if (!formElement) {
             element = document.getElementById(name + 'ValangValidator')
-        }
-        if (!element || element.tagName.toLowerCase() != 'script') {
+			if (element && element.tagName.toLowerCase() == 'script') {
+		        while (element && element.tagName.toLowerCase() != 'form') {
+		            element = element.parentNode
+		        }
+		        if (!element) {
+		            throw 'unable to find FORM element enclosing element with ID \'' + name+ 'ValangValidator\''
+		        }
+				formElement = element;
+			}
+		}
+		if (!formElement) { 
             throw 'unable to find form with ID \'' + name + '\' or script element with ID \'' + name + 'ValangValidator\''
         }
-        var foundElement = element
-        while (element && element.tagName.toLowerCase() != 'form') {
-            element = element.parentNode
-        }
-        if (!element) {
-            throw 'unable to find FORM element enclosing element with ID \'' + foundElement.id + '\''
-        }
-        return new ValangValidator.Form(element)
+        return new ValangValidator.Form(formElement)
     },
     _installSelfWithForm: function() {
         var oldOnload = window.onload
@@ -471,33 +477,29 @@
 // converting to a string or a number; number is always
 // favoured.
     _makeCompatible: function(lhs, rhs) {
-        try {
-            this._forceNumber(rhs)
-            return this._forceNumber(lhs)
-        } catch(ex) {
-        }
+    	var lhsNum = new Number(lhs)
+    	var rhsNum = new Number(rhs)
+    	if (lhsNum != Number.NaN && rhsNum != Number.NaN) {
+    		// Both are numbers - return primitive value of lhs
+    		return lhsNum.valueOf();
+    	}
+    	// at least one is NaN    	
         var lhsType = typeof lhs
         var rhsType = typeof rhs
         if (lhsType == rhsType) {
+        	// they're both the same (non-number) type, return lhs
             return lhs
-        } else if (lhsType == 'number' || rhsType == 'number') {
-            return this._forceNumber(lhs)
         } else {
             throw 'unable to convert [' + lhs + '] and [' + rhs + '] to compatible types'
         }
     },
     _forceNumber: function(value) {
-        if (typeof value != 'number') {
-            try {
-                var newValue = eval(value.toString())
-            } catch(ex) {
-            }
-            if (newValue && typeof newValue == 'number') {
-                return newValue
-            }
+    	var num = new Number(value);
+    	if (num == Number.NaN) {
             throw 'unable to convert value [' + value + '] to number'
-        }
-        return value
+    	} else {
+    		return num.valueOf();
+    	}
     },
 
 // Unary Operators


01
Aug
09

Spring MVC Validation using Commons Validator

This article will show you how to integrate Spring MVC with the commons validator framework.

Requirements:
If you have not done so already please read the following link to get started.

The commons validator is a core set of classes responsible for validating data. The down side of commons validation is that you can not use it directly out of the box. Typically there is some wrapper code that is necessary. Struts provided this wrapper code in the org.apache.struts.validator package.

Instead of packaging this code into Spring MVC the developers of the framework decided to included it as a module ontop of the Spring framework.

The spring validation module provides support modules that allows the commons validator to be integrated with the spring framework.

insert the following dependency in pom.xml and regenerate your eclipse project.

 		 <dependency>
			<groupId>org.springmodules</groupId>
			<artifactId>spring-modules-validation</artifactId>
			<version>0.8</version>
 		</dependency>
 		<dependency>
			<groupId>commons-validator</groupId>
			<artifactId>commons-validator</artifactId>
			<version>1.1.4</version>
 		</dependency>

This will get the spring-modules-validation onto your machine as well as commons-validator. The main class in the spring validator module is the FieldChecks this class contains almost all the necessary items that you may need to implement in your commons-validator code.

At the time of this writing the following were the methods and the necessary code to include into the validator-rules.xml file.

All the methods have the same signature except

  • requiredIf
  • requiredWhen

You need to include the following in whatever resource bundle your web application uses…

Read my article on Web Application Resource Bundles

   errors.required={0} is required.
   errors.minlength={0} can not be less than {1} characters.
   errors.maxlength={0} can not be greater than {1} characters.
   errors.invalid={0} is invalid.

   errors.byte={0} must be a byte.
   errors.short={0} must be a short.
   errors.integer={0} must be an integer.
   errors.long={0} must be a long.
   errors.float={0} must be a float.
   errors.double={0} must be a double.

   errors.date={0} is not a date.
   errors.range={0} is not in the range {1} through {2}.
   errors.creditcard={0} is an invalid credit card number.
   errors.email={0} is an invalid e-mail address.

This is what validator-rules will look like if you are using the Spring MVC framework with commons logging…

/WEB-INF/validator-rules.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE form-validation PUBLIC
          "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.1.3//EN"
          "http://jakarta.apache.org/commons/dtds/validator_1_1_3.dtd">

<form-validation>
	<global>
		<validator name="required"
			classname="org.springmodules.validation.commons.FieldChecks" method="validateRequired"
			methodParams="java.lang.Object,
                        org.apache.commons.validator.ValidatorAction,
                        org.apache.commons.validator.Field,
                        org.springframework.validation.Errors"
			msg="errors.required">
		</validator>
		<validator name="requiredif"
			classname="org.springmodules.validation.commons.FieldChecks" method="validateRequiredIf"
			methodParams="java.lang.Object,
                               org.springframework.validation.ErrorsAction,
                               org.apache.commons.validator.Field,
                               org.springframework.validation.Errors"
			msg="errors.required" />

		<validator name="validwhen" msg="errors.required"
			classname="org.apache.struts.validator.validwhen.ValidWhen" method="validateValidWhen"
			methodParams="java.lang.Object,
                       org.springframework.validation.ErrorsAction,
                       org.apache.commons.validator.Field,
                       org.springframework.validation.Errors" />

		<validator name="minlength"
			classname="org.springmodules.validation.commons.FieldChecks" method="validateMinLength"
			methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.springframework.validation.Errors"
			depends="" msg="errors.minlength"
			jsFunction="org.apache.commons.validator.javascript.validateMinLength" />

		<validator name="maxlength"
			classname="org.springmodules.validation.commons.FieldChecks" method="validateMaxLength"
			methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.springframework.validation.Errors"
			depends="" msg="errors.maxlength"
			jsFunction="org.apache.commons.validator.javascript.validateMaxLength" />

		<validator name="mask"
			classname="org.springmodules.validation.commons.FieldChecks" method="validateMask"
			methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.springframework.validation.Errors"
			depends="" msg="errors.invalid" />

		<validator name="byte"
			classname="org.springmodules.validation.commons.FieldChecks" method="validateByte"
			methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.springframework.validation.Errors"
			depends="" msg="errors.byte" jsFunctionName="ByteValidations" />

		<validator name="short"
			classname="org.springmodules.validation.commons.FieldChecks" method="validateShort"
			methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.springframework.validation.Errors"
			depends="" msg="errors.short" jsFunctionName="ShortValidations" />

		<validator name="integer"
			classname="org.springmodules.validation.commons.FieldChecks" method="validateInteger"
			methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.springframework.validation.Errors"
			depends="" msg="errors.integer" jsFunctionName="IntegerValidations" />

		<validator name="long"
			classname="org.springmodules.validation.commons.FieldChecks" method="validateLong"
			methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.springframework.validation.Errors"
			depends="" msg="errors.long" />

		<validator name="float"
			classname="org.springmodules.validation.commons.FieldChecks" method="validateFloat"
			methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.springframework.validation.Errors"
			depends="" msg="errors.float" jsFunctionName="FloatValidations" />

		<validator name="double"
			classname="org.springmodules.validation.commons.FieldChecks" method="validateDouble"
			methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.springframework.validation.Errors"
			depends="" msg="errors.double" />

		<validator name="date"
			classname="org.springmodules.validation.commons.FieldChecks" method="validateDate"
			methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.springframework.validation.Errors"
			depends="" msg="errors.date" jsFunctionName="DateValidations" />

		<validator name="intRange"
			classname="org.springmodules.validation.commons.FieldChecks" method="validateIntRange"
			methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.springframework.validation.Errors"
			depends="integer" msg="errors.range" />

		<validator name="floatRange"
			classname="org.springmodules.validation.commons.FieldChecks" method="validateFloatRange"
			methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.springframework.validation.Errors"
			depends="float" msg="errors.range" />

		<validator name="doubleRange"
			classname="org.springmodules.validation.commons.FieldChecks" method="validateDoubleRange"
			methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.springframework.validation.Errors"
			depends="double" msg="errors.range" />

		<validator name="creditCard"
			classname="org.springmodules.validation.commons.FieldChecks" method="validateCreditCard"
			methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.springframework.validation.Errors"
			depends="" msg="errors.creditcard" />

		<validator name="email"
			classname="org.springmodules.validation.commons.FieldChecks" method="validateEmail"
			methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.springframework.validation.Errors"
			depends="" msg="errors.email" />

		<validator name="url"
			classname="org.springmodules.validation.commons.FieldChecks" method="validateUrl"
			methodParams="java.lang.Object,
                       org.apache.commons.validator.ValidatorAction,
                       org.apache.commons.validator.Field,
                       org.springframework.validation.Errors"
			depends="" msg="errors.url" />
	</global>
</form-validation>

In the validation.xml all the forms that will be used in the application will be defined. The layout of this file is very similar to the validation-rules.xml however we don’t have have anything in the global section.

validation.xml

<!DOCTYPE form-validation PUBLIC
          "-//Apache Software Foundation//DTD Commons Validator Rules Configuration 1.1.4//EN"
          "http://jakarta.apache.org/commons/dtds/validator_1_1_4.dtd">
<form-validation>

	<formset>
		<form name="address">
			<field property="name" depends="required">
				<arg0 key="info.name" />
			</field>
			<field property="city" depends="required">
				<arg0 key="info.city" />
			</field>
			<field property="street" depends="required">
				<arg0 key="info.street" />
			</field>
			<field property="state" depends="required">
				<arg0 key="info.state" />
			</field>
			<field property="zip" depends="integer">
				<arg0 key="info.zip" />
			</field>
		</form>
	</formset>
</form-validation>

In the item above the first argument for each property is the key to the resource bundle entry that will display the error.

Insert the following into the resource bundle property file

nameEmpty=Name is empty
info.name=Name
info.city=City
info.street=Street Address
info.state=State

I needed to insert the following into my spring-servlet.xml and change the validator for my SimpleFormController to the commons validator.

<bean id="validatorFactory"
      class="org.springmodules.validation.commons.DefaultValidatorFactory">
<property name="validationConfigLocations">
	<list>
      <value>/WEB-INF/validation.xml</value>
      <value>/WEB-INF/validator-rules.xml</value>
    </list>
  </property>
</bean>

<bean id="beanValidator" class="org.springmodules.validation.commons.DefaultBeanValidator">
<property name="validatorFactory" ref="validatorFactory"/>
</bean>

That’s All!!! You now have a fully configured Spring MVC system with Commons Validator

Special Notes:
Currently the client side javascript code generation and validation is not working. This issue is discussed at the following url:
http://jira.springframework.org/browse/MOD-402
I would encourage you guys to register with that site and vote for the issue to get fixed.




Follow

Get every new post delivered to your Inbox.

Join 34 other followers