Fri, Aug 4, 2006
Having defined the domain model for the new Minerva system, which will be a user centric identity management system for UHI, I started to turn the UML into Spring beans and wire them together. The first sub system I wanted to implement and test was the feeder, that’s basically a Quartz scheduling system for extracting data from a data store, transforming it to the domain model and passing it to the main Minerva web services for addition to the identity management system.
Having used the Struts plug-in method before, to inject a controller facade into the context, I wanted to do the same with Spring but it’s not as straightforward. In fact there are a few hoops you must jump through to get there. The idea is to have a job controller, which runs a specified job at certain intervals, determined via the cron setup of Quartz and have that job use an extractor for getting data out of the data store. The UI lets you control the job via the job controller’s start/stop/pause methods etc. but how to give the Model access to a middle tier service such as this?
Evenutally I plumped for an ApplicationListener that is also ApplicationContextAware, called JobControllerInjector. This means it gets container events such as ContextRefreshedEvent and is also given the Spring ApplicationContext. Add setter for the job controller instance and wire it all together in the Spring definition file.
Then the problems started. JobControllerInjector was loaded twice, so each of it’s methods was called twice. How so? I scratched my head then found the answer. To use interfaces such as ApplicationListener and ApplicationContextAware you need to define a listener in web.xml:
<listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener>
but unless you specify an application context XML file, no application listeners or context aware beans will be loaded. This is because there are two contexts at work. The ApplicationContext and the WebApplicationContext. WebApplicationContext wants the WEB-INF/myapp-servlet.xml kind of files while ApplicationContext wants something different. So I pointed ContextLoaderListener to the feeder-servlet.xml file via a context param:
<context-param> <param-name>contextConfigLocation</param-name> <param-value>WEB-INF/config/feeder-servlet.xml</param-value> </context-param> This caused the double loading the beans. Once by the WebApplicationContext and once by the ApplicationContext. I was using a bad system. I should have disentangled my middle tier beans (ApplicationContext) from my normal beans (WebApplicationContext). To do so meant a couple of configs. One for the ApplicationContext:
<context-param> <param-name>contextConfigLocation</param-name> <param-value>WEB-INF/config/minerva.xml</param-value> </context-param> and one for the WebApplicationContext:
<servlet> <servlet-name>feeder</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>WEB-INF/config/feeder.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> That solved the problem and I can now go ahead and see how to inject the job controller into the context.