Friday, October 26, 2012

Method Parameter Names and Spring

Continuing on the previous blog entry about Constructor and method parameters and Java not retaining the parameter names at runtime - the previous entry was about constructor not retaining the parameter names and the implication of this for Contructor injections in Spring, here I will cover a few more scenarios where parameter names not being retained has implications with Spring:

1. Consider Spring MVC Controller method with a parameter to bind to a request parameter which is passed in:

@RequestMapping(value="/members/find")
 public String getMembersByName(@RequestParam String name){
  ...
  return "list";
 }

Here the parameter "name" has a @RequestParam annotation associated with it, which is in an indication to Spring MVC to bind a request parameter "name" to this method parameter.

Since the parameter name is not retained at runtime, it is likely that an exception will be thrown by Spring:

Request processing failed; nested exception is java.lang.IllegalArgumentException: Name for argument type [java.lang.String] not available, and parameter name i
nformation not found in class file either.

The fix here is simple, to either compile with debug options on which will retain the parameter names at runtime OR a better one is to simply indicate what the expected request parameter name is, as an argument to the @RequestParam annotation:

@RequestMapping(value="/members/find")
public String getMembersByName(@RequestParam("name") String name){
 return "list";
}


2. Along the same lines consider another Spring MVC controller method, this time supporting URI template patterns:

@RequestMapping(value="/members/{id}", method=RequestMethod.GET)
public @ResponseBody Member get(@PathVariable Integer id){
 return this.memberDB.get(id);
}

Here the expectation is that if a request comes in with a uri of /members/20, then the id parameter will get bound with a value of 20, however since at runtime the parameter name of "id" is not retained, the fix like in the previous case is either to compile with debug on, or to explicitly mention in the @PathVariable annotation what the expected pattern name is:

@RequestMapping(value="/members/{id}", method=RequestMethod.GET)
public @ResponseBody Member get(@PathVariable("id") Integer id){

3. A third example is with caching support in Spring with @Cacheable annotation. Consider a sample method annotated with @Cacheable:

@Cacheable(value="default", key="#param1.concat('-').concat(#param2)")
public String cachedMethod(String param1, String param2){
    return "" + new Random().nextInt();
}

Here the key is a Spring-EL expression, which instructs the keygenerator to generate the key by combining the argument of the first parameter of name param1 with argument to the second parameter with name param2. However the problem like before is that these names are not available at runtime.

One of the fixes, as before is to compile with debug symbols turned on. A second fix is to use placeholders to stand in for parameter index - a0 OR p0 for first parameter, a1 OR p1 for second parameter and so on, this way the @Cacheable key will look like this:
@Cacheable(value="default", key="#p0.concat('-').concat(#p1)")
public String cachedMethod(String param1, String param2){
    return "" + new Random().nextInt();
}



So in conclusion, a safe way to use Spring features that depend on method parameter names is to compile with debug on(-g or -g:var option of javac) or by explicitly passing in meta information that indicates what the parameter names are at runtime.

Sunday, October 21, 2012

Spring Constructor Injection and parameter names

At runtime, java classes do not retain the name of the constructor or method parameters, unless classes are compiled with debug options on. This has some interesting implications for Spring Constructor Injection:

Consider the following simple class:

package dbg;
public class Person {

 private final String first;
 private final String last;
 private final Address address;

 public Person(String first, String last, Address address){
  this.first = first;
  this.last = last;
  this.address = address;
 }

 public String getFirst() {
  return first;
 }

 public String getLast() {
  return last;
 }

 public Address getAddress() {
  return address;
 }
}

and a sample Spring bean configuration xml file:

<bean name="address1" class="dbg.Address" p:street1="street1" p:street2="street1" p:state="state1"/>
 <bean name="person1" class="dbg.Person" c:address-ref="address1" c:last="Last1" c:first="First1"  ></bean>
 <bean name="person2" class="dbg.Person" c:first="First2" c:address-ref="address1" c:last="Last2"   ></bean>

Here I am using the c namespace for constructor injection. This fails with the exception that the argument types are ambiguous - this is because the first argument is a String and since its runtime representation does not have the argument name present, Spring cannot determine if it should be substituted for the first name or last.

There are a couple of fixes possible for this scenario:

1. To use index based constructor injection, the drawback though is that it is very verbose:
<bean name="person1" class="dbg.Person" >
  <constructor-arg value="First1"></constructor-arg>
  <constructor-arg value="Last1"></constructor-arg>
  <constructor-arg ref="address1"></constructor-arg>
 </bean>
 <bean name="person2" class="dbg.Person" >
  <constructor-arg value="First2"></constructor-arg>
  <constructor-arg value="Last2"></constructor-arg>
  <constructor-arg ref="address1"></constructor-arg>
 </bean>


2. To compile with debug symbols on, this can be done by passing a -g or -g:var flag to the java compiler - this will ensure that the parameter names are preserved in the class file and the original concise bean configuration with c namespace will work.


3. A neat fix is to annotate the constructor with @ConstructorProperties which basically provides the argument names to Spring:

public class Person {

 private final String first;
 private final String last;
 private final Address address;

 @ConstructorProperties({"first","last","address"})
 public Person(String first, String last, Address address){
  this.first = first;
  this.last = last;
  this.address = address;
 }

This works with or without debug options turned on.

4. Probably the best fix of all is to simply use @Configuration to define the beans:
@Configuration
public static class TestConfiguration{
 
 @Bean
 public Address address1(){
  return new Address();
 }
 @Bean
 public Person person1(){
  return new Person("First1", "Last1", address1());
 }
 @Bean
 public Person person2(){
  return new Person("First2", "Last2", address1());
 }
 
}


Saturday, October 13, 2012

Spring Collection Merging

Spring collection merging is a feature that I first came across as an answer to a StackOverflow question

It is a way of creating a base collection (list, set, map or properties) and modifying this base collection in other beans, best explained using an example -

Consider a Person class with a field holding a list of addresses:

public class Person {
 private List<Address> addresses;
..
}

Assume that all Person instances have a common set of addresses, which can be specified using an abstract person bean this way:

<bean name="basePerson" class="Person" abstract="true">
 <property name="addresses">
  <list>
   <bean class="Address" p:street="Street1" p:state="State1" p:zip="001"></bean>
   <bean class="Address" p:street="Street2" p:state="State2" p:zip="002"></bean>
   <bean class="Address" p:street="Street3" p:state="State3" p:zip="003"></bean>
  </list>
 </property>
</bean>

Now, Person bean instances with addresses in addition to the addresses from the basePerson can be specified using Collection Merging feature this way - note the "merge=true" attribute of list:

<bean name="person1" class="Person" parent="basePerson">
 <property name="addresses">
  <list merge="true">
   <bean class="Address" p:street="Street4" p:state="State4" p:zip="004"></bean>
  </list>
 </property>
</bean>

Collection merging also works with <set/>, <map/> and <props/>

With Spring 3.1 and higher, a simpler option though could be simply to use @Configuraion and @Bean options, as then the merging can be directly handled with Java code, for eg, an equivalent @Configuration for the above xml bean configuration:

@Configuration
public class CollectionConfig{
 
 private List<Address> baseAddresses(){
  return Lists.newArrayList(new Address("Street1", "State1", "001"), new Address("Street2", "State2", "002"), new Address("Street3", "State3", "003"));
 }
 
 
 @Bean
 public Person person1(){
  Person person1 = new Person();
  person1.setAddresses(baseAddresses());
  person1.getAddresses().add(new Address("Street4", "State4", "004"));
  return person1;
 }
}


Saturday, October 6, 2012

Spring Autowire - Subtle behavior difference in xml and @Configuration

There is a subtle difference in the behavior of how autowire behaves with new @Configuration style of Spring bean configuration and using an xml file:

Consider a service into which a Dao is wired in:

public class SampleService {
 @Autowired private SampleDao sampleDao;
 
 public Sample getSample(int id){
  return this.sampleDao.findOne(id);
 }
}

Using an xml, the bean configuration would have been defined this way:

<bean name="sampleDao" class="SampleDaoImpl"/>
 <bean name="sampleService" class="SampleService"/>

and using the new @Configuration style it is defined this way:

@Configuration
public class AnnotationConfig {
 @Bean
 public SampleDao sampleDao(){
  return new SampleDaoImpl();
 }
 
 @Bean
 public SampleService sampleService(){
  return new SampleService();
 }
}

Now consider a program which uses the @Configuration defined bean configuration:

public static void main(String[] args) {
  ApplicationContext ctx = new AnnotationConfigApplicationContext(AnnotationConfig.class);
  SampleService sampleService = ctx.getBean(SampleService.class);
  System.out.println(sampleService.getSample(10));
 }

and a similar one using xml bean configuration:
public static void main(String[] args) {
  ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("springconfig.xml");
  SampleService sampleService = ctx.getBean(SampleService.class);
  System.out.println(sampleService.getSample(10));
 }

The behavior is that the SampleDao will not get correctly autowired into the SampleService in the case of xml based bean configuration but works correctly for @Configuration based bean configuration.

The reason for the behavior difference is that in the @Configuration case the AutowiredAnnotationBeanPostProcessor responsible for autowiring the fields is registered automatically whereas with XML bean configuration it has to be done explicitly. So the fix is to include AutowiredAnnotationBeanPostProcessor, which can be done in a few different ways, one of which is using:

<context:annotation-config/>

Note: I have deliberately demonstrated this in a main method instead of a unit test - In a unit test with Spring test support, the AutowiredAnnotationBeanPostProcessor is automatically registered by the test context and this behavior difference will not be seen there.

Thursday, October 4, 2012

Spring MVC - static resource handling with / servlet-mapping

A servlet mapping of "/" registers a "default" servlet - if a request comes in which matches a mapping in the web.xml file then the request will be handled by that servlet, however if no servlet is found matching a specific request pattern then the request is handled by the "default" servelt.

The "default" servlet is also responsible for handling the requests to the static content in a web application.

A convention for Spring MVC based applications is to register the Spring MVC Front Controller called the DispatcherServlet with a servlet mapping of '/'

    <servlet>
        <servlet-name>spring</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:/META-INF/spring/web/webmvc-config.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>


    <servlet-mapping>
        <servlet-name>spring</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

This enables Spring MVC to handle all the requests coming to the web application, except for cases where other specific mappings are available. A good example of a case where a specific mapping may be required is to configure the front controller for Apache CXF to accept webservice requests to /webservices servlet path:

 <servlet>
  <servlet-name>CXFServlet</servlet-name>
  <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>
 
 
 <servlet-mapping>
  <servlet-name>CXFServlet</servlet-name>
  <url-pattern>/webservices/*</url-pattern>
 </servlet-mapping>

Given that it is the "default" servlet which handles the request for the static resources like images, and the default servlet with Spring MVC is the DispatcherServlet, there are a few recommendations to serve out static content from Spring MVC based web applications:

1. Using mvc:resources -
For eg.
<mvc:resources mapping="/resources/**" location="/public-resources/"/>

This enables serving any static content whose location can be specified as a Spring Resource

2. Using mvc:default-servlet-handler:
 <mvc:default-servlet-handler />
This provides a way to serve out the static content from the root of the web application even though the Dispatcher Servlet is registered at /, the details of how Spring does this is available at the Spring documentation site here - in brief the responsibility is delegated to the containers default servlet.