MemeStorm

Exploring the Spring Framework and Application Development

Basic Spring Web services with XFire and JSR 181

Posted in All by Jon on the May 30th, 2006

I hadn’t intended to expose any of our functionality with Web services so soon, but that’s the great thing about Spring—many of the services are orthogonal to each other. Let’s look at how we can expose some Spring beans as Web services. We’ll do it using JSR 181 too, which means that the Web service implementation is so easy that even a Ruby hacker could write it…;-)

Web Services and JSR 181

Look here for an introduction to JSR 181 and where it fits in to the world of Web services. You need not though, as it’s pretty simple. First, let’s define the interface to our simple Web service:

package com.memestorm.ws;

import javax.jws.WebService;

@WebService( targetNamespace = “http://memestorm.com/skeleton1/AuthorService” )
public interface AuthorService {

	public String sayHello();

}

That’s it. Note the nice @WebService annotation. It’s all that’s really necessary. I threw in the targetNamespace attribute to show how you can modify things. The system will automatically generate a WSDL file - and using these attributes (and others) you can modify the generated WSDL. Now that we have the interface, let’s implement it!

package com.memestorm.ws;

import javax.jws.WebService;

@WebService(
  serviceName = "MyAuthorService",
  endpointInterface = "com.memestorm.ws.AuthorService"
)
public class AuthorServiceImpl implements AuthorService {

  public String sayHello() {
    return "Hello World Services!";
  }
}

Oh my, that was easy….There’s nothing to say, except:

  • Do you see how there aren’t any external dependencies in this code? The only thing I import is the annotation…this is cool…
  • This is just a POJO - which we’ll be defining in a Spring context file. So we can easy wire it up with other beans using IoC.
  • This is an example of CoC again—you don’t have to do much other than write @WebService.

Okay, now that we’ve written our Web service - and you can write as many as you like of course, let’s look at how to wire it all together.

Hooking XFire into your Spring Application

I used XFire as the Web services stack here. It implements some nice things that I don’t find in Apache Axis, such as the JSR 181 stuff, multiple XML binding mechanisms, multiple transports (including Jabber & JMS) etc. You only have to hook it into your web application once. After that, simply create your POJOs and expose them. What you need to do is:

  1. Create the XFire Spring context configuration file. It’s here that you expose your POJOs, and where you can wire in other Spring beans etc.
  2. Modify the Web application web.xml to deploy the XFire servlet
  3. Modify your build process to include all the right JARs

Let’s look at these in order.

1. XFire Configuration File

Now XFire’s pretty powerful. You can configure just about anything, and much of this you can do from the configuration file. It’s also where you expose your POJOs. The following file, xfire-servlet.xml, contains a little of both:

<beans>

 <!--  add your beans here -->
 <bean id=”annotatedAuthorService”
     class=”com.memestorm.ws.AuthorServiceImpl”/>
 <!–  fin  –>

 <bean id=”webAnnotations”
     class=”org.codehaus.xfire.annotations.jsr181.Jsr181WebAnnotations”/>
 <bean id=”handlerMapping”
     class=”org.codehaus.xfire.spring.remoting.Jsr181HandlerMapping”>
        <property name=”typeMappingRegistry”>
            <ref bean=”xfire.typeMappingRegistry”/>
        </property>
        <property name=”xfire”>
            <ref bean=”xfire”/>
        </property>
        <property name=”webAnnotations”>
            <ref bean=”webAnnotations”/>
        </property>
 </bean>

 <bean class=”org.springframework.web.servlet.handler.SimpleUrlHandlerMapping”>
        <property name=”urlMap”>
            <map>
                <entry key=”/”>
                    <ref bean=”handlerMapping”/>
                </entry>
            </map>
        </property>
    </bean>

    <import resource=”classpath:org/codehaus/xfire/spring/xfire.xml”/>

</beans>

The lines in bold are the only ones that matter to us - it exposes our bean implemented by com.memestorm.ws.AuthorServiceImpl. The rest configures XFire to use annotations in the first place and sets up some defaults. You will want to come back here when you do more advanced stuff such as type mappings.

2. Configuring the XFire Servlet

As you’ve seen, we’ve got this xfire-servlet.xml context configuration file. You’ll want to tell Spring about it, and as we’ve shown before, you can do this by modifying the contextConfigLocation context parameter in web.xml:

 <context-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>
     /WEB-INF/applicationContext.xml /WEB-INF/jdbcContext.xml
     /WEB-INF/xfire-servlet.xml
   </param-value>
 </context-param>

You also need to add the following lines:

   <servlet>
     <servlet-name>XFireServlet</servlet-name>
     <servlet-class>
       org.codehaus.xfire.spring.XFireSpringServlet
     </servlet-class>
   </servlet>
   <servlet-mapping>
      <servlet-name>XFireServlet</servlet-name>
      <url-pattern>/servlet/XFireServlet/*</url-pattern>
   </servlet-mapping>
   <servlet-mapping>
     <servlet-name>XFireServlet</servlet-name>
     <url-pattern>/services/*</url-pattern>
   </servlet-mapping>

That simply ensures that XFire is deployed as a servlet.

3. Build

XFire has an astounding array of dependencies - and you only need some under various circumstances. Here’s how I had to modify the build file to account for this:

  • Modify the master-classpath so that I had the right JARs in my path to compile everything:
     <fileset dir="${xfire.root}">
       <include name="xfire-all-1.1.jar" />
     </fileset>
     <fileset dir="${xfire.root}/lib">
       <include name="xfire-jsr181-api-1.0-M1.jar" />
     </fileset>
  • Modify the libraries target to ensure all run-time JARs are available:
    <fileset dir="${xfire.root}">
      <include name="xfire-all-1.1.jar" />
    </fileset>
    <fileset dir="${xfire.root}/lib">
      <include name="stax-api-1.0.1.jar" />
      <include name="xfire-jsr181-api-1.0-M1.jar" />
      <include name="activation-1.1.jar" />
      <include name="mail-1.4.jar" />
      <include name="jaxen-1.1-beta-8.jar" />
      <include name="jdom-1.0.jar" />
      <include name="wsdl4j-1.5.2.jar" />
      <include name="wstx-asl-2.9.3.jar" />
    </fileset>

Okay, I admit there is a little pain here. But you only need to do this once of course, and you are deploying an awesome Web service stack that will bring you hours of pleasure…

4. Running it

That’s it though - grab the source below, run build, deploy, and you’ll have exposed the web service. The XFire servlet provides a nice way to view the deployed Web services too. If you go to http://localhost:8080/skeleton2/services/ you’ll see:

 Services:
  o MyAuthorService [wsdl]

with a link to the WSDL file. Viewing the WSDL file you’ll get the expected output:

<wsdl:definitions xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:soap11="http://schemas.xmlsoap.org/soap/envelope/" xmlns:soap12="http://www.w3.org/2003/05/soap-envelope" xmlns:soapenc11="http://schemas.xmlsoap.org/soap/encoding/" xmlns:soapenc12="http://www.w3.org/2003/05/soap-encoding" xmlns:tns=”http://memestorm.com/skeleton1/AuthorService” xmlns:wsdlsoap=”http://schemas.xmlsoap.org/wsdl/soap/” xmlns:xsd=”http://www.w3.org/2001/XMLSchema” targetNamespace=”http://memestorm.com/skeleton1/AuthorService”>
  <wsdl:types>
    <xsd:schema targetNamespace=”http://memestorm.com/skeleton1/AuthorService” elementFormDefault=”qualified” attributeFormDefault=”qualified”>
      <xsd:element name=”sayHello”>
        <xsd:complexType />
      </xsd:element>
      <xsd:element name=”sayHelloResponse”>
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element name=”out” type=”xsd:string” nillable=”true” minOccurs=”1″ maxOccurs=”1″ />
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
    </xsd:schema>

……

  <wsdl:service name=”MyAuthorService“>
    <wsdl:port binding=”tns:MyAuthorServiceHttpBinding” name=”MyAuthorServiceHttpPort”>
      <wsdlsoap:address location=”http://localhost:8080/skeleton1/services/MyAuthorService” />
    </wsdl:port>
  </wsdl:service>
</wsdl:definitions>

Nice huh? No hassle Web services…

A Client

Okay, here’s a client that you can run against the Web service. Read the XFire documentation to learn more about it if you like:

package com.memestorm.ws.client;

import org.codehaus.xfire.service.Service;
import org.codehaus.xfire.client.XFireProxyFactory;
import org.codehaus.xfire.annotations.AnnotationServiceFactory;
import com.memestorm.ws.*;

import java.net.MalformedURLException;

public class Test {
	public static void main(String[] args) {
		Service serviceModel = new AnnotationServiceFactory()
				.create(AuthorServiceImpl.class);
		try {
			AuthorService service = (AuthorService) new XFireProxyFactory().create(
					serviceModel,
					"http://localhost:8080/skeleton1/services/MyAuthorService");
			String s = service.sayHello();
			System.out.println(s);

		} catch (MalformedURLException e) {
			e.printStackTrace();
		}

	}
}

Summary

What we’ve done here is set up a scenario where you can easily expose POJOs as Web services using JSR 181 standard annotations. These are standard annotations that you’ll find in Java EE 5 - so it’s pretty nice to be able to use them here in the XFire Web services stack.

Moreover, this scenario is hooked into our Spring web application. We can trivially add additional POJOs, and use the full Spring toolbox to manipulate them.

Download

Here’s a JAR of all the source, with instructions on how to build and deploy:
skeleton2-1.zip

Acknowledgements

I’d like to reference the Logemann Blog that showed me how to do most of this stuff.


Print This Post Print This Post

Spring Components - XML configuration on steroids

Posted in All by Jon on the March 29th, 2006

The single most important feature in Spring 2.0 (IMO) is a new XML
configuration convenience feature. I believe this is going to
revolutionize the way people develop for Spring and bootstrap a modest
component market too. This is a lot more than a convenience features
- it’s a fledgling component model too.

In the ‘old’ Spring configuration files, almost everything was a bean. That is, you would write:

<bean id="foo" class="bar">
   <!-- zip zip -->
</bean>

In fact, if you look at the DTD you’ll see why:

<!ELEMENT beans (
	description?,
	(import | alias | bean)*
)>

In Spring 2 (I’m using M3) you can do something altogether different:

 <mycomponent:foobar att="zip"/>

Note that there is no class attribute - all the necessary information is captured behind the namespace. If you look at it from an XML configuration point of view, what this provides is a way of avoiding laboriously repetitive bean definitions by capturing beans in their own namespace. Spring comes with a number of these already defined:

 <jndi:lookup id="dataSource" jndiName="jdbc/MyDS"/>
 <tx:advice id="txAdvice">
   <tx:attributes>
     <tx:method name="insert*"/>
     <tx:method name="update*"/>
     <tx:method name="*" read-only="true"/>
   </tx:attributes>
 </tx:advice>

However, now think of the possibilities:

  • You can simplify configuration (as we’ve just seen)
  • You can hide implementation (all the ‘user’ needs to see is the namespace)
  • You can start distributing ‘Spring Components’ that folk can simply plug in by adding the JAR and referencing them from your application context!

I think it’s the last two points that are the most exciting. They’re perhaps not what the Spring developers intended, but they are the start of something new. You can now distribute a simple JAR that contains all your Spring beans that do something, say interface with Amazon, together with an XML schema describing those beans that you want to make public. Any user of this ‘Spring Component’ simply installs this JAR and starts typing:

<amazon:listBooks conditionSubject='memetics' associateId='memestorm'/>

You can even bung view tier stuff into beans, so the future is wide open.

How to do it

Let’s look at how you can roll your own component, resulting in a
simple JAR that you can distribute. See the attachment for a deployable
example: simple.jar. If this is in your application’s classpath, for example deployed in WEB-INF/lib then at the end of the day, all you need to do is wire it into your application context.

Wiring in the component

<beans
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:memestorm=”http://www.memestorm.com/schema/simple”
    xsi:schemaLocation=”http://www.springframework.org/schema/beans
     http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.memestorm.com/schema/simple
     http://www.memestorm.com/schema/simple/spring-simple.xsd”>

  <memestorm:action id=”s” value=”HelloWorld” />

  <bean id=”foo”>
    <!– .. –>
  </bean>
  <!– other definitions –>

</beans>

Notice that I’m using the XML Schema version of the bean configuration instead of DTD, and that I’ve defined my memestorm component as a namespace mapped to http://www.memestorm.com/schema/simple. We’ll later see how this name space gets mapped to a bean.

Coding the component

That’s it as far as the client goes. Just add the JAR and wire it in. Let’s look at the implementation. Instead of an Amazon component, we’ve written a rather simpler Simple component:

package com.memestorm.beandemo;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class SimpleBean {
   private String value;
   private String otherValue;

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

   public SimpleBean () {
      logger.error("In the constructor of SimpleBean");
   }

   public void action() {
      logger.error("In action with value='" + value + "' and otherValue='" + otherValue + "'");
   }

   public String getValue() {
      return value;
   }

   public void setValue(String value) {
      this.value = value;
   }

   public String getOtherValue() {
      return otherValue;
   }

   public void setOtherValue(String otherValue) {
      this.otherValue = otherValue;
   }
}

As you can see, it has two properties (value, otherValue) and its constructor and action() method both log messages. (So to use the component, you need commons-logging in your app!)

Defining its XML interface

As you saw in the wiring, the XML interface is defined by an XML schema. So we need to define an XML schema that should be adhered to by clients of our component. Here is one we created, simple.xsd:

<xsd:schema xmlns="http://www.memestorm.com/schema/simple"
         xmlns:xsd="http://www.w3.org/2001/XMLSchema"
         targetNamespace="http://www.memestorm.com/schema/simple"
         elementFormDefault="qualified"
         attributeFormDefault="unqualified">
   <xsd:element name="action">
      <xsd:complexType>
         <xsd:attribute name="id" type="xsd:ID" use="required"/>
         <xsd:attribute name="value" type="xsd:string"/>
      </xsd:complexType>
   </xsd:element>
</xsd:schema>

As you can see, it simply permits two attributes to an element called action. Look back to ‘Wiring in the component’ and you’ll see it all fits together.

The Namespace Handler

Behind the scenes, Spring invokes a namespace handler to create the actual bean instance from something like:

<memestorm:action id="s" value="HelloWorld" />

As a component developer, you have to supply this namespace handler. Here is our implementation:

package com.memestorm.tag;

import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSimpleBeanDefinitionParser;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
import org.w3c.dom.Element;
import com.memestorm.beandemo.SimpleBean;

public class SimpleNamespaceHandler extends NamespaceHandlerSupport {

  public SimpleNamespaceHandler() {
    registerBeanDefinitionParser(”action”, new SimpleBeanDefinitionParser());
  }

  private static class SimpleBeanDefinitionParser extends
      AbstractSimpleBeanDefinitionParser {

    protected Class getBeanClass(Element element) {
      return SimpleBean.class;
    }

    protected void postProcess(BeanDefinitionBuilder definitionBuilder,
        Element element) {
      definitionBuilder.addPropertyValue(”otherValue”, “fromPostProcess”);
      definitionBuilder.setInitMethodName(”action”);
    }
  }
}

Note that the constructor registers a bean definition parser, associating it with an element. You’ll later see how we map the namespace to the element. The bean definition parser itself is pretty simple. getBeanClass() should return the appropriate class - as you can see we return SimpleBean.clas. We also have a postProcess() method that you can use to add to the parsed bean definition. In this case we set another property value and we set the init-method to be invoked. What’s happening behind the scenes here, in AbstractSimpleBeanDefinitionParser, is that the XML snippet is parsed and all of its attributes (and values) are mapped to setters of properties on the bean instance (except for id). In other words, its programmatically creating the bean definition and its properties according to the values defined in the application context.

Packaging

To package everything, simply include all the XSD files, beans, and namespace handlers into a JAR with two special files in the META-INF directory. First you need spring.schemas:

http://www.memestorm.com/schema/simple/spring-simple.xsd=com/memestorm/tag/simp
le.xsd

As you can see, this maps the namespace location to the physical location of the schema in the JAR. Next, you need spring.handlers:

http://www.memestorm.com/schema/simple=com.memestorm.tag.SimpleNamespaceHandler

As you can see, this maps the schema namespace to the tag handler. That’s it.

Summary

In summary then:

  • Create the XML schema defining your component
  • Create your component bean/beans
  • Create a namespace handler that maps the schema to the beans
  • Create two packaging artifacts to tie it all together

Download

Here’s a deployable JAR with source (of course, you need to deploy to a Spring 2 M3 application, with commons-logging available too).

simple.jar


Print This Post Print This Post

Convention over Configuration in Spring’s MVC

Posted in All by Jon on the March 17th, 2006

Convention Over Configuration (CoC) is a term often bandied
around by Ruby on Rails followers. From the wikipedia it’s
defined as “the programmer only needs to specifically configure what
is unconventional.” Very sensible advice indeed, although it’s not without its problems. We’ve already
seen one example
of CoC in Spring: the use of
InternalResourceViewResolver effectively lets programmers
forget about configuring resource views for incoming URLs, and instead
rely on the convention of mapping to a like-named view (it extracts
the view name from the URL). In fact, the MultiActionController is a type of CoC too - you don’t have to configure an action name; Spring tries to automatically invoke a like-named method on the controller class based on the URL. (The MethodNameResolver actually.) These two essentially enforce the convention of automatically selecting a view, and a method in a controller class respectively.

Now let’s look at a third example of CoC: ControllerClassNameHandlerMapping. This enforces the convention of automatically selecting a controller.

Background

As shown in Simple URL Mapping…, a URL Mapper has to map incoming URL requests to particular controllers. Typically we have to configure the URL mapper with each new controller that we add. Turning this on its head, we want to use CoC so that the convention is that the correct controller is automatically selected if it’s registered, without any configuration.

How to do it

Simply use the ControllerClassNameHandlerMapping as your URL mapper in your dispatch-servlet.xml configuration file. For example:

<bean id="urlMapping"
  class="org.springframework.web.servlet.mvc.ControllerClassNameHandlerMapping">
</bean>

Define your controllers as usual. For example, I have this:

<bean id="commandController"
   class="com.memestorm.web.CommandController">
</bean>

That’s it. You don’t have to wire your controllers to URLs, this is now automated. Now instead of explicitly configuring mappings between URLs and controllers, you can rely on the convention, which is to take the ClassUtils.getShortName() name of the controller class, remove the “Controller” suffix if it exists and the cap, and use the result as a mapping. For example our CommandController would be mapped to /command/.

For MultiActionController controllers (see our recent example) it works slightly differently. In this case, say for our DispatchController example, it will map /dispatch/* to the controller. (Note the extra wildcard).

Now you can concentrate on writing your controllers and action methods. Everything will be automatically wired in; nice.

How it works

It’s actually dead simple. The ControllerClassNameHandlerMapping class simply iterates through the application context looking for beans of type Controller, adding them to the mapping as described earlier.

Acknowledgements

Thanks to Matt for highlighting this class.


Print This Post Print This Post

Spring Binding and Validation: Introduction

Posted in All by Jon on the February 20th, 2006

You want to slurp in some data, perhaps from a simple form or from
request parameters. To do this, we’ll explore the AbstractCommandController and learn about the related issues of binding and validation. Spring makes this kind of thing pretty effortless, and this will serve as the foundation of exploring more advanced form-handling.

How to do it

The controllers we wrote about in previous blog entries, such as the MultiActionController, are generally used for carrying out actions. What if you wanted to rather input some data first, perhaps through request parameters? Well one way to do this is the hard way:

String val = (String) request.getParameter("someParamName");

Those were the days. Untyped, inflexible, error-prone, with all sorts of casting (imagine if I wanted a date or an integer).

A much better approach use data binding - let Spring bind the incoming data to a Plain Old JavaBean (POJO) for you - then you can simply use that POJO as you’d expect. Spring calls these beans commands, which I think is a bit of a misnomer, but there you go. Let’s see this in action. First, we’re going to modify our dispatch-servlet.xml configuration file to include a new controller:

<bean id="commandController"
  class="com.memestorm.web.CommandController">
</bean>

Now let’s modify our handler mappings (see previous blog entries if you need to understand these) to include a mapping to our command controller:

/cmd=commandController
/*=dispatchController

So in theory now, something like http://localhost:8080/send/cmd?age=10 will get sent to our command controller. Now the command controller we’ll use is the simplest around. It extends the AbstractCommandController. Here’s the code:

public class CommandController extends AbstractCommandController {

  public CommandController() {
    setCommandClass(SimpleCommand.class);
  }

  protected ModelAndView handle(HttpServletRequest request,
		HttpServletResponse response, Object command,
                BindException errors) throws Exception {

    SimpleCommand cmd = (SimpleCommand) command;

    PrintWriter pw = response.getWriter();
    pw.println(cmd);
    return null;
  }
}

The first thing you’ll notice (probably because I have it in bold) is that the constructor calls setCommandClass() with the class that will hold the incoming values. This is the command class I spoke about earlier. All incoming parameters are mapped onto this class, automatically, for you. We pass the setCommandClass() the class so that it can instantiate a new instance every time the controller is invoked. Now take a look at the handle() method, somewhat more complicated than the others that we’ve seen in Basic Spring Web MVC Example. The two extra parameters are the command (which Spring will have created and populated for you), and an object listing errors (which we’ll describe later).

The method is pretty simple: It casts the command object to the expected type, and then writes it to the response. Let’s look at the command object:

public class SimpleCommand {
 Integer age;
 String name;

 public String getName() {return name;}

 public void setName(String name) {this.name = name;}

 public Integer getAge() {return age;}

 public void setAge(Integer age) {this.age = age;}

 public String toString() {
   return "Name=[" + this.name + "], Age=[" + this.age + "]";
 }
}

You must admit, that’s pretty straightforward. A typed container, just ready to be populated by incoming values. Now, if you invoke something like http://localhost:8080/skeleton3/send/cmd?name=Jon&age=30 you’ll see the output as Name=[Jon], Age=[30]. This is pretty darn nice for a number of reasons:

  • Notice that I didn’t write any code to bind the incoming parameters to the command bean. It’s automatic.
  • Notice that the command bean is typed - getAge() returns an Integer and somehow Spring converted the incoming field to the appropriate type automatically.

This is a considerable step up from getRequestParameter(). It even works for a form input. For example, here is a simple form that you can use to invoke our controller:

<form action="send/cmd" method="post">
 Name: <input type="text" name="name" />
 Age: <input type="text" name="age" />
</form>

But wait, there’s more! If we extend our handler() method to dump some information found in the errors parameter, we’ll see further magic:

pw.println("<hr>" + "Error count=" + errors.getErrorCount());
pw.println("<br>Errors for age field"  + errors.getFieldErrors("age"));
pw.println("<br>Input value for age field: " + errors.getFieldValue("age"));
pw.println("<br>" + errors.getGlobalErrors());

Now if we invoke it with an error in the age value (for example, some letters instead of digits) as in: http://localhost:8080/skeleton3/send/cmd?name=Jon&age=crikey we’ll see the following output:

Name=[Jon], Age=[null]
<hr>Error count=1
<br>Errors for age field[Field error in object 'command' on field 'age': rejected value [crikey]; codes [typeMismatch.command.age,typeMismatch.age,typeMismatch.java.lang.Integer,typeMismatch]; arguments [MessageSourceResolvable: codes [command.age,age]; arguments []; default message [age]]; default message [Failed to convert property value of type [java.lang.String] to required type [java.lang.Integer] for property 'age'; nested exception is java.lang.NumberFormatException: For input string: "crikey"]]
<br>Input value for age field: crikey

Now notice that the age value is null (it can’t convert ‘crikey’), notice that we have an error count, that we have access to the original text that the user entered, and we have a good error message too. That’s a lot of magic for very little effort.

But wait, there’s even more. With a simple change to the constructor, we can validate input values too. Here is an example modification:

public CommandController() {
  setCommandClass(SimpleCommand.class);
  setValidator(new Validator(){
    public boolean supports(Class clazz) {
      return (clazz.equals(SimpleCommand.class));
    }
    public void validate(Object command, Errors errors) {
      SimpleCommand incoming = (SimpleCommand) command;
      ValidationUtils.rejectIfEmpty(errors, “age”, “required.age”);
      if (incoming.getAge() != null && incoming.getAge() > 30)
        errors.reject(”over.the.hill”, “too old”);
   }});
}

As you can see, we’ve set a validator. A validator simply implements the Validator interface—here we’ve done it with a simple anonymous class. You can instead write full classes for the validators and wire them in with the web context instead - in that way you can promote some reuse with your validators too. The validator interface has two methods. supports() tells the system what kind of class it validates. The validate method performs the actual validation. We make use of ValidationUtils to ensure that the age is not empty. You can, instead, write your own validation as we do checking that the age is not too high. So now, if we pass in an age of 34 say, we’ll get the following output:

...
[Error in object 'command': codes [over.the.hill.command,over.the.hill]; arguments []; default message [too old]]

There is in fact a little more magic too (message interpretation) but that will have to wait for another day.


Print This Post Print This Post « Previous PageNext Page »