Spring Roo, and mixing xml and annotated dependency injection of a Property

I used Spring Roo (with Spring 3.0) to create a simple project with a few domain objects and controllers. I wanted to experiment with using the @Component annotation and autowiring, but was having severe problems trying to autowire a String value (the url of a service) into my @Component managed bean, the string being contained in the xml of the application context. After searching several blogs, this is what I found:

  1. it is not possible to autowire a primitive value using annotations
  2. it WAS not possible before Spring 3, now you can do it with Spring Expression Language (spEL)

Here was my next to last attempt:

webmvc-config.xml:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xmlns:p="http://www.springframework.org/schema/p"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:util="http://www.springframework.org/schema/util"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
				
	<!-- The controllers are autodetected POJOs labeled with the @Controller annotation. 
	<context:component-scan base-package="org.tcl" use-default-filters="false">
		<context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
	</context:component-scan> -->

	<!-- The controllers are autodetected POJOs labeled with the @Controller annotation. -->
	<context:component-scan base-package="org.tcl" />
	<context:annotation-config/>
	
	<!-- Turns on support for mapping requests to Spring MVC @Controller methods
	     Also registers default Formatters and Validators for use across all @Controllers -->
	<mvc:annotation-driven/>

        <util:properties id="appProperties" location="classpath:/app.properties"/> 
...

TclApiService.java:

package org.tcl.service;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class TclApiService implements ApiService {

	@Value("#{appProperties['tclApiUrl']}") String apiUrl;
	
	@Override
	public ApiResponse call() {
		return null; //breakpoint here, just to check out the value of the apiUrl property 
	}

	public void setApiUrl(String apiUrl) {
		this.apiUrl = apiUrl;
	}

}

app.properties:

tclApiUrl=http://www.mydomain.org/api/courses

No matter WHAT I tried, the tc server startup would fail with errors such as:

No matching bean of type [java.lang.String] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}


or, in the case of my final spEL attempt:

Field or property 'appProperties' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext'

Turns out, this is what is happening:
Since the <context:component-scan> tag occurs before the “appProperties” bean definition in webmvc-config.xml, the bean definition is not available when the @Component-ized class is being constructed. The fix is simple, just move the “appProperties” bean definition above any <context:component-scan> tag. In Roo, keep in mind, at least in my case, that there were actually 2 <context:component-scan> tags, one in applicationContext.xml and one in webmvc-config.xml. Putting my properties bean definition above the one in applicationContext.xml solved the problem. Tc Server now started successfully and the property was populated in my @Component class.

Nowhere in official spring documentation could I find the stipulation that it actually matters where the <context:component-scan> tag is placed. Initially I thought that spring might maintain a map of unresolved dependencies which are satisfied in a round robin fashion, but nope, the xml dependencies are resolved first, then the annotated ones – at least in this simple case.

oracle.sql.TIMESTAMP ClassCastException with SqlRowSet

After 2.5 days of smashing my head against Oracle hell (mixed metaphors?), I finally found a workaround for this strange ClassCastException I was having. I only found 2 or 3 Google results with this odd ClassCastException, so decided that my experience was significant enough to share!

ORIGINAL GOAL:
We had some code resembling the following:

String sqlString = “{some SQL statement that contains an ORDER BY clause}”;
Item item = itemDao.read(itemId);
getJdbcTemplate().query(sqlString,
    new Object[]{itemId},
    new RowCallbackHandler() {
        public void processRow(ResultSet rs) throws SQLException {
            ItemActivity itemActivity = new ItemActivity();
            populateItemActivity(itemActivity, rs, item);
        }
});
 

And populateItemActivity just does some stuff to the ItemActivity instance passed in, like:

public void populateItemActivity(ItemActivity itemActivity, ResultSet rs, Item item) throws SQLException{         
    itemActivity.setFileName(item.getName());
    itemActivity.setDate(rs.getTimestamp("LAST_MODIFIED_TIME"));
    itemActivity.setSize(item.getFileSize());      
    itemActivity.setAuditName(rs.getString("AUDIT_NAME"));
}

We discovered that the “AUDIT_NAME” field value was not populated for all returned rows, and in that case, the ItemActivity object should be populated with the “AUDIT_NAME” value in the record just before it, ordered by itemId and date. My fear was that the processRow(ResultSet rs) callback method was not guaranteed to process rows in the same order that they appeared in the resultset, so my attempt to copy back from the immediate prior record to copy its “AUDIT_NAME” value would be foiled and possibly incorrect.

Looking for a method that I could manipulate the entire ResultSet at one time, and so control the iteration and set a variable that I could copy between row processing, I found the JdbcTemplate.queryForRowSet(String sql, Object[] args) method, which returns a SqlRowSet I thought I could use to iterate explicitly.

We happen to be using Oracle, and a Timestamp value is one of the values in the returned SqlRowSet. This code was throwing the ClassCastException:

public void populateItemActivity(ItemActivity itemActivity, SqlRowSet rs, Item item) throws SQLException{                  
    itemActivity.setFileName(item.getName());
    oracle.sql.TIMESTAMP timestamp = (oracle.sql.TIMESTAMP) rs.getObject("LAST_MODIFIED_TIME ");  <--ClassCastException
    Date date =  (timestamp == null) ? null : new Date(timestamp.dateValue().getTime());   
    itemActivity.setDate(date);
    itemActivity.setSize(item.getFileSize());
    itemActivity.setAuditName(rs.getString("AUDIT_NAME"));
}

NOTE: the code at line 3 and 4 above I got from http://jira.springframework.org/browse/SPR-4886, who appeared to be having the same issue with SqlRowSet returning an oracle.sql.TIMESTAMP instead of a java.sql.Date with a call to rs.getObject.

No matter what I tried, I could not resolve the ClassCastException. Debugging in Eclipse showed that the Object returned from rs.getObject() was a oracle.sql.TIMESTAMP, but casting it to that same class resulted in the ClassCastException. Infuriating! Even gathering other developers, we could not figure this out.

So, NEW GOAL:
So I changed tack and found this other JdbcTemplate.query method with a different signature using a ResultSetExtractor, which allowed me to deal with the entire recordset (it's a ResultSet now instead of a SqlRowSet with the other attempt) at once (instead of a callback method for each row), letting me handle the row iteration explicitly:

getJdbcTemplate().query(sql,
        new Object[]{vaultItem.getVaultItemId()},
        new ResultSetExtractor() {                                             
          public Object extractData(ResultSet rs) throws SQLException,
            org.springframework.dao.DataAccessException {
                 String auditNameBuffer = null;
                 while (rs.next()){
                      ItemActivity itemActivity = new ItemActivity();                                                        
                      String auditName = rs.getString("AUDIT_NAME");
                      if ((null != auditName) ){
                          auditNameBuffer = auditName;
                      }                                                               
                      itemActivity.setAuditName(auditName);
                 }
                 return null;
            }      
     }
);

Project completed, but still don’t know what caused the ClassCastException.