Spring Transactions and the Element

   Posted by: Jim White

This week, I had a wonderful group of people from the IT department of one of Minnesota's great counties in my classroom to learn Spring 3.  It's always nice to see a group of people that seem to get along well in the classroom.  My guess is they also get along well at work and that usually bodes well for software projects.  Teams that work together usually find solutions together.

A couple of very good questions from class this week centered on the schema-based (XML) configuration of Spring transactions.

Spring Transactions in XML

In general, when using Spring's declarative schema-based transactions, you need to define three elements in the XML metadata along with the beans that need transactions (unless these are defined with annotations and component scans).  The XML below is an example of defining a few beans and the three elements needed to apply Spring's schema-based AOP transactions.

<!-- A couple of business beans that need transactions -->
<bean id="contactDao" class="com.intertech.service.ContactDaoJDBCImpl"
    destroy-method="dispose" autowire="byType">
</bean>
<bean id="contactService" class="com.intertech.service.ContactServiceImpl"
    autowire="byType">
</bean>
<!-- A transaction manager bean -->
<bean id="txManager"
    class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="basicDS" />
</bean>
<!-- The transaction advice - setting attributes for transactions -->
<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
        <tx:method name="get*" read-only="true" />
        <tx:method name="add*" propagation="REQUIRED" />
        <tx:method name="update*" propagation="REQUIRED" isolation="SERIALIZABLE" />
        <tx:method name="remove*" propagation="REQUIRED" isolation="SERIALIZABLE" />
        <tx:method name="*" />
    </tx:attributes>
</tx:advice>
<!-- Establish the AOP transaction cross cutting concern and define which classes/methods are transactional  -->
<aop:config>
    <aop:pointcut id="serviceOperations"
        expression="execution(* com.intertech.service.*.*(..))" />
    <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceOperations" />
</aop:config>

Beyond the two beans (id's of contactDao and contactService) that will be provided transactions, the txManager bean defines the transaction manager to be used.  Spring provides several transaction manager beans which act as facades to the actual technology-specific transaction implementations.  The transaction manager beans must implement org.springframework.transaction.PlatformTransactionManager, which allows custom and new transaction managers to facilitate transactions in Spring without developers having to learn and understand that manager directly.  In the case above, I am just using Spring's DataSourceTransactionManager bean that wraps the transaction manager capability provided via JDBC and the available data source.  See here for more information on defining transaction managers.

Next is the all important transaction advice element.  This element sets the transaction manager to use for transactions (as defined by the previous transaction manager bean in this case), and then establishes the transaction attributes for various transactional methods.  This is the portion of the configuration I want to focus on in this post.  More on this in just a bit.

The last of the 3 elements defining schema-based transactions in Spring is the the AOP configuration element.  As Spring's declarative transactions are performed by AOP, this element defines the points of execution for transactions (and other cross cutting concerns) across the application.  In this example, the AOP transaction advisor and associated pointcut expression say that transactions are to be applied to any method in any class in the com.intertech.service package (regardless of return type, method name, method parameters, etc.).  See here for more information on AOP configuration definitions.

Transaction Attributes

Back to the transaction advice element, you will note that the <tx:attributes> element contains any number of child <tx:method> elements.  These elements lead to a great deal of confusion.

<tx:attributes>
    <tx:method name="get*" read-only="true" />
    <tx:method name="add*" propagation="REQUIRED" />
    <tx:method name="update*" propagation="REQUIRED" isolation="SERIALIZABLE" />
    <tx:method name="remove*" propagation="REQUIRED" isolation="SERIALIZABLE" />
    <tx:method name="*" />
</tx:attributes>

How do these elements relate to the pointcut defined in the AOP configuration?  How do the wildcards and matches work on these elements?  Does the name="*" containing element always have to be there?

AOP Configuration Pointcut and <tx:method>

First off, those classes and methods captured by the AOP configuration element and its associated pointcut for the transaction advisor define which methods in all the methods of the Spring beans may have transactions applied to them.  If you will, this represents a potentially large set of methods, but smaller than the original set provided by all beans in the application.  Importantly, however, this does not mean that the methods captured by the AOP pointcut are going to have transactions yet.

image

Next, the transaction advice element with its transactional attributes (and nested transactional method elements) determine which methods will indeed be transactional and how the transactions on those methods will operate.  In the example above, there are several <tx:method> elements that select subsets of the AOP pointcut methods.  The methods captured by the wild card matches (all methods in this group that start with "get", start with "add", start with "remove", etc....) as shown in the yellow ovals in the diagram below become transactional.

image

Wildcards and Order of <tx:method>

The order of the <tx:method> elements is not important.  What is important is the name pattern!  Spring attempts to match the most specific name patterns first.  So, for example, say you had just three <tx:method> elements as shown below.

<tx:method name="add*" propagation="SUPPORTS" read-only="true" />
<tx:method name="addC*" propagation="REQUIRED" timeout="30"/>
<tx:method name="*" />
Then also assume you had a method called addContact( ) and another called addRate( ) that fell under the methods of the AOP pointcut.  In this case, the second <tx:method> element above would be applied to addContact( ) method and the first <tx:method> would apply to the addRate( ) method.  That is because more specific addC* pattern matches addContact but not addRate.  Further, even though the "*" pattern would capture both the addContact( ) and addRate( ) methods, it is more general and less specific than either of the "add*" or "addC*" names.  The name="*" pattern is important, however, to capture all methods selected by the AOP pointcut, but not by a more specific <tx:method> name.  In this example, a method like foo( ) that was captured by the pointcut would not be captured by either add* or addC* patterns.

The Attributes

Remember, the <tx:method> elements determine what is transactional, but also determine what attributes are applied to the transactions of those methods captured in the pattern match.  More information can be found on the transaction attributes here.  However, below is a table listing the attributes and their default values.  When your <tx:method> element does not specify the attribute, the method's transaction uses the default value.  In the example above, the catch-all <tx:method name="*"/> element abides by all defaults.

Transaction attribute

Purpose

Default setting

propagation

What should happen if a transaction already exists?

REQUIRED

isolation

Set the isolation level of the transaction.

DEFAULT

read-only

Specify if data can be modified or only read in the transaction.

False (allowing for reads and writes)

timeout

Length of time in seconds that the transaction can run before timing out.

-1

rollback-for

What triggers a rollback of a transaction?

RuntimeException

no-rollback-for

What does not trigger a rollback of a transaction?

Any checked exception

Wrap Up

I want to thank Kevin and Bryan from my class this week for raising the good questions which led to the material for this post.  I had a lot of good questions to choose from this week as everyone in my class was engaging and thoughtful.  It makes class a lot of fun for everyone - students and instructor alike.  If you are looking to learn more about Spring 3, contact Dan McCabe at Intertech, Inc. to enroll in a class today.  You can find more information about our Complete Spring 3 Framework class here.

Clarifying Spring Framework IDs and Names

   Posted by: Jim White

All Spring beans must have an identifier. It says so right in the Spring documentation:  "Every bean has one or more identifiers. These identifiers must be unique within the container that hosts the bean."

Identifier Confusion

Unfortunately, the fact that a Spring bean can have an "id" and a "name" makes things confusing.  Making matters worse, creating two beans with the same identifier (id or name) doesn't necessarily lead to an exception.  Huh?  Say what?  An example is in order to help clarify.  Assume, as shown below, you have two beans (foo and bar) defined in a Spring beans XML configuration file.

   1: <?xml version="1.0" encoding="UTF-8"?>
   2: <beans xmlns="http://www.springframework.org/schema/beans"
   3:     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   4:     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
   5:     <bean id="foo" class="com.intertech.Foo">
   6:     </bean>
   7:     <bean id="bar" class="com.intertech.Bar">
   8:     </bean>
   9: </beans>

Code to get a bean, in this case the foo bean, might look like this sample.

   1: FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext(new String[] { "springbeans.xml" });
   2: Foo f = (Foo) context.getBean("foo#");

As you may or may not know, you can use name in place of (or in addition to) id when defining the beans.  Per the Spring 3 documentation, "you use the id and/or name attributes to specify the bean identifier(s)."

   1: <?xml version="1.0" encoding="UTF-8"?>
   2: <beans xmlns="http://www.springframework.org/schema/beans"
   3:     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   4:     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
   5:     <bean name="foo" class="com.intertech.Foo">
   6:     </bean>
   7:     <bean name="bar" class="com.intertech.Bar">
   8:     </bean>
   9: </beans>

This doesn't change any of the Java code.  So the question is why?  Why have a name instead of a bean id?  Why have an id and a name?  The reason is that bean ids correlate directly to the XML element id and the XML specification limits the characters that can be used in ids.  Special characters like #, @, $, *, /, and more are not allowed in the id attribute.  If you add one of these special characters to the id, you get an XmlBeanDefinitionStoreException thrown when the configuration is read by the container.  The exception indicates the id "is not a valid value for 'NCName'."

So the name attribute functions just like the id attribute on a bean - uniquely identifying the bean - but it allows the bean unique identifier to contain special characters.  These special characters can be especially helpful in certain types of mapping.  For example, the "/" character can be very useful in identifying certain beans in MVC and RESTful situations.

Only 1 id, but Many names

The name attribute allows for many "aliases."  That is a collection of identifiers that can be used to identify the bean.  Below, for example, the foo bean is also identified by "myFoo", "kingBean", and "notBar" names.  Multiple bean names need only be separated by comma, semi-colon or whitespace.  According to the Spring documentation "A bean will almost always have only one id, but if a bean has more than one id [name], the extra ones can essentially be considered aliases."  So bean names are, if you will, "extra" identifiers.

   1: <bean id="foo" name = "myFoo,kingBean,notBar" class="com.intertech.Foo">
   2: </bean>

Again, the Java code doesn't care which id or name is used.  The following code all results in the container returning a Foo instance in each case.

   1: Foo f1 = (Foo) context.getBean("foo");
   2: Foo f2 = (Foo) context.getBean("kingBean");
   3: Foo f3 = (Foo) context.getBean("notBar");
   4: Foo f4 = (Foo) context.getBean("myFoo");

Names Still Must Uniquely Identify

However, the names must still uniquely identify a bean.  When you try to reuse the name (as shown below, with two beans having the name "kingBean") or when the id of a bean matches the name of another bean, an BeanDefinitionParsingException - Bean name 'kingBean' is already used in this file - is thrown by the container when the configuration is read.

   1: <bean id="foo" name="myFoo,kingBean,notBar" class="com.intertech.Foo">
   2: </bean>
   3: <bean id="bar" name="kingBean" class="com.intertech.Bar">
   4: </bean>

Non-unique Bean Id or Name Across Bean Files (and what to do about it)

Where things get a little interesting is when bean definitions are spread out across many configuration files.  Say, for example, the Foo bean is defined in beans1.xml while the Bar bean is defined in beans2.xml.

beans1.xml

   1: <beans xmlns="http://www.springframework.org/schema/beans"
   2:     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   3:     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
   4:     <bean id="foo" name="myFoo,kingBean,notBar" class="com.intertech.Foo">
   5:     </bean>
   6: </beans>
beans2.xml
   1: <beans xmlns="http://www.springframework.org/schema/beans"
   2:     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   3:     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
   4:     <bean id="bar" name="kingBean" class="com.intertech.Bar">
   5:     </bean>
   6: </beans>

Now, if the id or name of either bean (in this case the kingBean name) is non-unique, you surprisingly get no exception thrown by the container!!!

   1: FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext(
   2:         new String[] { "beans2.xml", "beans1.xml" });
   3: //works fine
   4: Foo f = (Foo) context.getBean("kingBean");

What is the "secret sauce" that makes this work?  Or maybe stated more appropriately - Why doesn't this break?  The documentation says "the last bean definition with the same id [or name] wins, respectively to the order of the xml files.'  In the example Java code above, beans1.xml is loaded by the container after beans2.xml.  Therefore, beans1.xml wins in identifying the bean with the name kingBean.  Had the files been read in reverse order, then kingBean would identify a Bar object.

   1: FileSystemXmlApplicationContext context = new FileSystemXmlApplicationContext(
   2:         new String[] { "beans1.xml", "beans2.xml" });
   3: Bar b = (Bar) context.getBean("kingBean");

If this sounds scary to you, you can turn off this default behavior and make cause bean ids/names to be unique across all configurations of the container.  Look into the DefaultListableBeanFactory for how to the setAllowBeanDefinitionOverriding( ) method.  You should also read this post.

Wrap Up

Hope this helps you better understand Spring bean identification.  If you are interested in learning more about Spring or other Java topic, take a look at Intertech's course curriculum.  Give us a week to improve your skill set. 

Secrets of the Spring AOP Proxy

   Posted by: Jim White

By Jim White (Director of Training and instructor)

Spring Aspect Oriented Programming (AOP) is a powerful mechanism to weave cross cutting concerns like security, transactions, exception handling, logging, etc. into business code ("core concerns") without explicitly adding calls to the cross cutting concern.  This allows the cross cutting concern to be updated, removed, swapped out, and generally maintained in a much simpler fashion.

AOP Under the Covers - the Proxy

Having said that, because of the way AOP is implemented, there can seem to be a certain amount of mystery and magic at work.  Your code never invokes the advice methods of an aspect class.  Spring does it for you based on your XML configuration or annotations on the aspect class.  How does the magic happen?  Under the covers, Spring implements a proxy to intercept calls to a target object. 

As an example, say you have a service bean that invokes a call to the saveCustomer( ) method on a DAO.

image

Now say you want to have some logging (a cross cutting concern) occur when a call to any save method occurs on a DAO.  Spring detects your need to call on a logging aspect through your AOP configuration or annotations.  When it does, it builds a proxy (called CustomerDaoProxy for example sake here) around the "target" object - in this case the DAO.

image

Now, on a call to a save method in the DAO, the proxy intercepts the call and routes it appropriately to the appropriate advice method in the aspect class.

image

Issues Given Proxy-based AOP

This proxy-based mechanism allows powerful cross cutting concern code to be isolated from business code.  However, there are all kinds of "gotcha's" that can occur with this type of under-the-covers implementation of which you should be aware.  First of all, aspects cannot advise other aspects.  Per the Spring documentation:

Advising aspects with other aspects?

In Spring AOP, it is not possible to have aspects themselves be the target of advice from other aspects. The @Aspect annotation on a class marks it as an aspect, and hence excludes it from auto-proxying.  (See:  static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/aop.html#aop-at-aspectj).

This can have some big implications in, sometimes even limitations on, how you implement a cross cutting concern.  Say, for example, your form of logging required adding log statements to a database.  Further, say this database entry required to be performed as part of a transaction (probably a reasonable assumption).  If transactions were implemented through aspects, then you would have the log aspect a target of advice from a transaction aspect.  That's a big "NGH" - not gonna happen.

image

Another such "gotcha" is that local or internal calls to methods within a target object do not cause the advice method to be invoked even with both methods are captured by the AOP pointcut.  In other words, a call from a method in a target to another method in the target does not get intercepted by the proxy.  This results in the advice method never being triggered.  For example, assume you had some sort of business bean that made a call to itself as shown in the simple example below.

   1: @Component("mySubordinate")
   2: public class CoreBusinessSubordinate {
   3:     
   4:     public void doSomethingBig() {
   5:         System.out.println("I did something small");
   6:     }
   7:     
   8:     public void doSomethingSmall(int x){
   9:         System.out.println("I also do something small but with an int");    
  10:     }
  11: }

Also assume you had an aspect constructed and configured with a pointcut capturing both CoreBusinessSubordinate methods as the CrossCuttingConcern class shown here.

   1: @Component
   2: @Aspect
   3: public class CrossCuttingConcern {
   4:     
   5:     @Before("execution(* com.intertech.CoreBusinessSubordinate.*(..))")
   6:     public void doCrossCutStuff(){
   7:         System.out.println("Doing the cross cutting concern now");
   8:     }
   9: }

If another bean was to explicitly call both the target doSomethingBig( ) and doSomethingSmall( ) methods directly, the proxy/interceptor of the aspect would cause the advice to fire twice - as expected.

   1: @Service
   2: public class CoreBusinessKickOff {
   3:     
   4:     @Autowired
   5:     CoreBusinessSubordinate subordinate;
   6:  
   7:     // getter/setters
   8:     
   9:     public void kickOff() {
  10:         System.out.println("I do something big");
  11:         subordinate.doSomethingBig();
  12:         subordinate.doSomethingSmall(4);
  13:     }
  14: }

Results of calling kickOff above given code above.

I do something big
Doing the cross cutting concern now
I did something small
Doing the cross cutting concern now
I also do something small but with an int

However, if instead of calling the doSomethingSmall( ) method from another bean, it was called from inside the target as shown in this next example, the advice fires only one time!

   1: @Component("mySubordinate")
   2: public class CoreBusinessSubordinate {
   3:     
   4:     public void doSomethingBig() {
   5:         System.out.println("I did something small");
   6:         doSomethingSmall(4);
   7:     }
   8:     
   9:     public void doSomethingSmall(int x){
  10:         System.out.println("I also do something small but with an int");    
  11:     }
  12: }
  13:  
  14: --------------------------------------------
  15:  
  16: public void kickOff() {
  17:     System.out.println("I do something big");
  18:     subordinate.doSomethingBig();
  19:     //subordinate.doSomethingSmall(4);
  20: }

New results of calling kickOff above.

I do something big
Doing the cross cutting concern now
I did something small
I also do something small but with an int

The internal call from the target method to another target method escapes the aspect interceptor.

Work Around

You can sometimes find a work around for AOP proxy issues.  For example, you can work around this last issue by exposing the proxy to the target.  When configuring autoproxy development in your configuration, add an expose-proxy attribute and set its value to true (by default it is false).

   1: <aop:aspectj-autoproxy expose-proxy="true"/>

Now in your target code, you can reroute self-invocating methods back through the target proxy.  Use AopContext.currentProxy( ) to get the proxy object and then invoke the internal target method through the proxy as shown below.

   1: public void doSomethingBig() {
   2:     System.out.println("I did something small");
   3:     //doSomethingSmall(4);
   4:     ((CoreBusinessSubordinate) AopContext.currentProxy()).doSomethingSmall(4);
   5: }

Of course, take heed that this tightly couples your core business concern to AOP - something you are trying to avoid with AOP to begin with.  So it may be better to refactor the code such that internal calls are not required when you need advice to fire on all methods.

Wrap Up

I owe a "thanks for the great question" credit to one of my students (Johannes) from my latest Spring  class.  Through some of his experimentation and questions in class, he provided the inspiration for this blog post.  Or as I like to say in class, he won the "stump-the-chump" award this week with yours truly serving as the chump.  If you would like to learn more about Spring please consider taking Intertech's Complete Spring Framework 3 class (see here).  Please contact Dan McCabe for further details.

Spring Framework, JSR-303 Validation and Custom Messages

   Posted by: Jim White

By Jim White (Director of Training and Instructor)

In Minnesota where Intertech is located, spring comes a little later that in other parts of the country.  This year, I noticed our flowering trees finally came out in full force this week.  ?Bout time!  It seemed very fitting that I spent the week teaching Spring 3 to a great group of people.

On the last day of class, I got a question from Michelle that I wasn?t able to answer as it was at the very end of our class.  She wanted to know how to customize the error message displayed on validation errors triggered by JSR-303 annotations in beans.  Great question and I want to use this post to provide her a proper answer that I didn?t get a chance to provide.

Spring 3 and JSR-303 Bean Validation API

As of Spring 3.0, the framework fully supports the JSR-303 Bean Validation API.  JSR-303 standardizes validation and allows you to declare validation constraints on bean properties via annotation.  An example of a bean annotated with some validation constraints (@Size, @NotNull, @Past, @Min, @Max) is shown below.

   1: public class Contact {
   2:     private Long id = 0L;
   3:     @Size(min = 1)
   4:     private String firstName;
   5:     @NotNull
   6:     @Size(min = 1)
   7:     private String lastName;
   8:     @Past
   9:     private Date dateOfBirth;
  10:     private boolean married;
  11:     @Min(0)
  12:     @Max(20)
  13:     private int children;
  14:     ...
  15: }

For more information about JSR-303 and the bean validation API, see here.  You can also find a good tutorial blog on some of the annotations here.

Custom Error Messages

Michelle?s question centered on how to provide a custom error message on a JSP that uses a bean like Contact as its command bean.  A default message is displayed when a property if found in violation of the constraint.  For example, here is the form to edit/add a Contact.

   1: <form:form action="addcontact.request" method="post"
   2:     commandName="contact">
   3:     <table border="1">
   4:         <tr>
   5:             <th>&nbsp;</th>
   6:             <th><spring:message code="editcontact.heading"/></th>
   7:         </tr>
   8:         <tr>
   9:             <td bgcolor="cyan"><spring:message code="editcontact.label.firstname"/></td>
  10:             <td><form:input path="firstName" size="40" /><font
  11:                 color="#FF0000"><form:errors path="firstName*" /></font></td>
  12:         </tr>
  13:         <tr>
  14:             <td bgcolor="cyan"><spring:message code="editcontact.label.lastname"/></td>
  15:             <td><form:input path="lastName" size="40" /><font
  16:                 color="#FF0000"><form:errors path="lastName*" /></font></td>
  17:         </tr>
  18:         <tr>
  19:             <td bgcolor="cyan"><spring:message code="editcontact.label.dob"/></td>
  20:             <td><form:input path="dateOfBirth" size="40" /><font
  21:                 color="#FF0000"><form:errors path="dateOfBirth*" /></font></td>
  22:         </tr>
  23:         <tr>
  24:             <td bgcolor="cyan"><spring:message code="editcontact.label.married"/></td>
  25:             <td><form:checkbox path="married" /><font color="#FF0000"><form:errors
  26:                 path="married" /></font></td>
  27:         </tr>
  28:         <tr>
  29:             <td bgcolor="cyan"><spring:message code="editcontact.label.children"/></td>
  30:             <td><form:input path="children" size="5" /><font
  31:                 color="#FF0000"><form:errors path="children*" /></font></td>
  32:         </tr>
  33:         <tr>
  34:             <td><input type="submit" value="<spring:message code="editcontact.button.save"/>" /></td>
  35:             <td><input type="reset" value="<spring:message code="editcontact.button.reset"/>" /></td>
  36:         </tr>
  37:     </table>
  38:     <a href="index.jsp"><spring:message code="editcontact.link.mainpage"/></a>
  39: </form:form>

Without any changes to the defaults, the error messages displayed on the form are less than pleasing.image

Option 1 ? add a message parameter to the annotation

The first option to correcting this issue and providing your own custom error message is to add a message parameter to the validation annotations.  Some sample error messages are shown in the code below along with a picture of the same form using those messages.

   1: public class Contact {
   2:     private Long id = 0L;
   3:     @Size(min = 1, message="Contact first name is required.")
   4:     private String firstName;
   5:     @NotNull(message="Contact cannot be left empty.")
   6:     @Size(min = 1, message="Contact last name is required.")
   7:     private String lastName;
   8:     @Past(message="Contact date of birth must be a date in the past.")
   9:     private Date dateOfBirth;
  10:     private boolean married;
  11:     @Min(value=0, message="A contact cannot have fewer than 0 children")
  12:     @Max(value=20, message="A contact cannot have more than 20 children")
  13:     private int children;
  14:     ...
  15: }

image

Option 2 ? Use the Message Bundle

While providing custom messages right in the bean with the validations is convenient, you may elect to use the custom messages in your applications message resource bundles.  Using a resource bundle allows your application support internationalization/localization concerns.

To setup an application to use resource message bundles, add the following bean to your Spring bean configuration file.

   1: <bean id="messageSource"
   2:     class="org.springframework.context.support.ResourceBundleMessageSource">
   3:     <property name="basenames">
   4:         <list>
   5:             <value>messages</value>
   6:         </list>
   7:     </property>
   8: </bean>

In this example, the default message bundle file would be messages.properties (located somewhere on the application?s classpath).

You can override the default validation annotation messages in the messages.properties by specifying the annotation and the new default message as shown below.

   1: #example messages.properties
   2: #override @Size default message
   3: Size=Your must provide an entry.
   4: #override @Min default message
   5: Min=Your entry is too small.
   6: #override @Max default message
   7: Max=Your entry is too large.
   8: #override @Past default message
   9: Past=Your entry must be a date in the past.

image

This is nice, but you will notice that the messages are not very custom to the context.  Since the same annotation may be used in multiple locations (i.e. on multiple beans), the message must be kept fairly generic.

You can further customize the messaging and still keep it external to the code by using dot notation with the property and/or command bean name and property to further specify which message to use from the bundle.  In the example below, messages specific to firstName and lastName fields are provided while also providing more specific contact bean date of birth and children messages.

   1: #example messages.properties
   2: #override @Size default message
   3: Size.firstName=First names cannot be empty.
   4: Size.lastName=Last names cannot be empty.
   5: #override @Min default message
   6: Min.contact.children=You must specify a number of children >= 0.
   7: #override @Max default message
   8: Max.contact.children=You must specify a number of children <= 20.
   9: #override @Past default message
  10: Past.contact.dateOfBirth=Birth dates must be a date in the past.

image

My thanks to Michelle for providing an excellent question.  Hope this helps Michelle and others looking to customize JSR-303 validation messages in Spring.

Need Spring Training?

If you or someone you work with could benefit from Spring 3 training, please visit our web site and sign up today for Complete Spring 3 Framework.  You can also contact Dan McCabe ? head of training sales ? at DMcCabe@intertech.com.

Small but Powerful - Spring 3 Type Conversion System

   Posted by: Jim White

By Jim White (Director of Training and Instructor)

Its fall already!  Where did the summer go?  Its been almost a year since Spring 3.0 was released.  Have you and your organization migrated to this new platform yet?  I suspect if you  have not, you will be doing so shortly.  Spring 3 is loaded with new features - some big and some small.  Some features, like Spring Expression Language, are bigger and take some time to learn and use.  Others are quite simple and can be incorporated quickly.

Type Conversion System

Take the new type conversion system as an example.  This is a relatively small feature that can really help convert data between types.  In particular, it can be used to convert String data found in Spring XML configuration files to the appropriate data type.  In this way, it serves as an alternative to the PropertyEditors and CustomEditorConfigurers of past Spring versions. 

To understand how the type conversion system works, say you wanted to configure Customer beans in XML.  The Customer class has an ssn property of type CustomerSSN (shown below).

public class CustomerSSN {
    private int area;
    private int group;
    private int serial;
    public CustomerSSN(int a, int g, int s) {
        this.area = a;
        this.group = g;
        this.serial = s;
    }
    public int getArea() {
        return area;
    }
    public void setArea(int area) {
        this.area = area;
    }
    public int getGroup() {
        return group;
    }
    public void setGroup(int group) {
        this.group = group;
    }
    public int getSerial() {
        return serial;
    }
    public void setSerial(int serial) {
        this.serial = serial;
    }
}

You want to dependency inject SSN data into the Customer objects, but how do you get string data, like that shown below, into a CustomerSSN object?

<bean id="example" class="Customer">
    <property name="ssn" value="456-21-7890" />
</bean>

Type conversion system to the rescue!

The Converter Interface

At the heart of this new system is the Spring Framework provided Converter interface.  The Converter interface is typed to allow you to convert from any one type to another.

public interface Converter<S, T> { }

The S is the source type and T is the target type.  The Converter interface is very simple. It requires implementing classes to have just one method - a convert( ) method.  The convert method must take a parameter of the source type and return a reference of the target type.  Here is an example implementation of Converter to convert String objects to CustomerSSN objects.

import org.springframework.core.convert.converter.Converter;
public final class SSNConverter implements Converter<String, CustomerSSN> {
    public CustomerSSN convert(String source) {
        int area = Integer.parseInt(source.substring(0, 3));
        int group = Integer.parseInt(source.substring(4, 6));
        int serial = Integer.parseInt(source.substring(7, 11));
        return new CustomerSSN(area, group, serial);
    }
}

A ConversionService

Behind the scenes, a ConversionService executes type conversion logic at runtime.  Simply register your Converters with a Spring ConversionService bean to allow it to use your Converter.  A Spring-provided default ConversionService bean converts between strings, numbers, enums, collections, maps, and other common types.  You can provide configuration - through a ConversionServiceFactoryBean - to register your Converters or override the default converters with the ConversionService.  In the example below, the SSNConverter is registered.

<bean id="conversionService" class=
        "org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <list>
            <bean class="com.intertech.SSNConverter" />
        </list>
    </property>
</bean>

That?s all there is to handling conversion!  Now, your XML configuration can use SSN-formatted strings and they will be converted to CustomerSSN objects automatically.  In the case below, when the example Customer bean is instantiated, it will be dependency injected with a CustomerSSN object with area property set to 456, group property set to 21 and serial property set to 7890.

<bean id="example" class="Customer">
    <property name="ssn" value="456-21-7890" />
</bean>

Importantly, since the Converters are just another object, you can define them as beans and dependency inject them anywhere conversion might be needed.  This makes them more versatile that the old PropertyEditors.  You can learn more about the type conversion system here.

Spring 3 Class Coming Soon

I have been updating Intertech's Complete Spring class and it should be out around the holiday season.  It will include sections on the new Spring 3 annotations, Spring Expression Language, REST support, improved MVC framework, and much much more.  The class will now be 5 days long and packed with labs to give you real hands on experience you can take to work the next week.  Go to www.intertech.com to learn more about our course offerings and stay tuned for more details on new classes coming out this fall/winter.

Find Us
Contact Us 651-288-7000 1-800-866-9884
Home | Training | Curriculum | Course Finder | Schedule | Enroll | Twin Cities Java User Group | Consulting | Foundation | Jobs | About Us | Our Story | Press Room | Instructors | President | Map & Directions | Sitemap

Java Training | JSF / Struts / Spring / Hibernate Training | Java Power Tools Training | .NET 4.0 & Visual Studio 2010 Training | Microsoft Web Development Training | Prism / MVVM / MEF Training | .NET 3.5 and Visual Studio 2008 Training | .NET 2.0 and Visual Studio 2003 Training | Cloud Computing Training | Ajax / Web Services / XML Training | Groovy and Grails Training | SQL Server 2012 Training | SQL Server 2008 Training | SQL Server 2005 Training | Mobile Development Training | SharePoint 2010 Training | SharePoint 2007 Training | Agile, Process, Analysis & Design Training | Arch/Design Patterns Training | Microsoft Official Curriculum Training | Web Development Training | Ruby Training | Rational Application Developer (RAD) Training | WebSphere Application Server Training | WebSphere Portal Training | WebLogic Training | Boot Camp Training | Project Management Training | C / C++ Training | Metro / WinRT / Windows 8 Development Training | Retired

Intertech delivers training on-site and virtually serving cities including Phoenix, AZ | San Francisco, CA | Los Angeles, CA | San Diego, CA | San Jose, CA | Washington, DC | Chicago, IL | Orlando, FL | Boston, MA | Duluth, MN | Minneapolis St. Paul, MN | Rochester, MN | Raleigh-Durham, NC | New York, NY | Philadelphia, PA | Austin, TX | Dallas, TX | Houston, TX | Seattle, WA.