Showing posts with label spring. Show all posts
Showing posts with label spring. Show all posts

Monday, 26 July 2010

Spring Schemas

I ran into an interesting problem with Spring and ActiveMQ a few days ago. To make use of the ActiveMQ namespace in our Spring contexts, I had the following beans element:
  xmlns="http://www.springframework.org/schema/beans"
amq="http://activemq.apache.org/schema/core"
xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemalocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core-5.1.0.xsd">

...

This seemed fairly sensible since we're using version 5.1.0 of ActiveMQ and there IS a schema at http://activemq.apache.org/schema/core/activemq-core-5.1.0.xsd.

However, when this was deployed to one of our test environments that does NOT have access to the internet, it fell over with the following exception:
Offending resource: class path resource [appContext.xml];
nested exception is org.springframework.beans.factory.xml.XmlBeanDefinitionStoreException:
Line 10 in XML document from class path resource [messagingContext.xml] is invalid;
nested exception is org.xml.sax.SAXParseException: cvc-complex-type.2.4.c:
The matching wildcard is strict, but no declaration can be found for element 'amq:connectionFactory'
This led me to discover a feature that I was previously unaware of - the spring.schemas file. The documentation says this:
The properties file called 'spring.schemas' contains a mapping of XML Schema locations (referred to along with the schema declaration in XML files that use the schema as part of the 'xsi:schemaLocation' attribute) to classpath resources. This file is needed to prevent Spring from absolutely having to use a default EntityResolver that requires Internet access to retrieve the schema file.
In fact, the default spring.schemas file contains the following entries:
http\://www.springframework.org/schema/beans/spring-beans-3.0.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd
http\://www.springframework.org/schema/beans/spring-beans.xsd=org/springframework/beans/factory/xml/spring-beans-3.0.xsd
So both those URL's are mapped to schemas on the classpath, ie. Spring doesn't need to download them from the internet.

However, the spring.schemas file bundled with ActiveMQ contains these entries:
http\://activemq.org/config/1.0=activemq.xsd
http\://activemq.org/config/1.0/1.0.xsd=activemq.xsd
http\://activemq.apache.org/schema/core=activemq.xsd
http\://activemq.apache.org/schema/core/activemq-core.xsd=activemq.xsd
So although mappings are provided, the mappings are for URL's that don't actually exist. (To be honest, I can't see how/why you would use these mappings unless you were already aware of this feature!)

Nonetheless, I fixed the issue by changing the beans declaration as follows:
  xmlns="http://www.springframework.org/schema/beans"
amq="http://activemq.apache.org/schema/core"
xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemalocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://activemq.apache.org/schema/core http://activemq.apache.org/schema/core/activemq-core.xsd">

...

Note that the URL for the ActiveMQ namespace now matches a URL in spring.schemas.

Tuesday, 25 August 2009

Paying The Bills

A distinct lack of activity on the blogging front lately. This is because I've been (a) moving house and (b) actually enjoying myself at work! ;-)

As Tech Lead on a brand new project, I've been writing a web app using Spring MVC, Freemarker + Hibernate and I thought I would share a few thoughts. I should start by saying that it's been nowhere near as easy as Grails, GSP + GORM, but nonetheless, it's a big improvement on the 'bad old days' of Java web app development.

To be honest, although Spring MVC is both powerful and flexible, I've always found it a little fiddly in the past. However, this time I decided to use annotation-driven controllers and these were way easier to work with than the MultiActionController I've used before. All you need is something like this in your context:
<context:component-scan package="com.royale.sam.web.controller">
And then you can annotate your controller class like this:
@Controller
@RequestMapping(method = RequestMethod.GET)
public class PageController {

...

@RequestMapping(value={"/page.html", "/item.html"})
public ModelAndView pageHandler(HttpServletRequest request, HttpServletResponse response) {
...
}
}
And that's it! Using this technique you can group handler methods in one controller (much like you can in Grails) which makes it easier to reuse domain logic and leads to a well organised application. We found this was particularly useful when implementing handlers for our AJAX functionality.

Freemarker was also pretty good, although - possibly because it's application agnostic - we found that it didn't support a number of features that you take for granted when using JSP. (In fact, I would've used JSP, but requirements dictated that we used a templating language.) For example, we couldn't find an easy + reliable way to get the context path, so we ended up introducing a servlet filter that made this information (and more) available to every page in our app. We also steered clear of using taglibs in our Freemarker templates, preferring to write our own directives.

And Hibernate was Hibernate. Always trickier than you expect to set it up, but once it's done, you generally don't have to worry about it anymore.