AppFuse Refactorings Part II: Spring Integration
I took some time last weekend and refactored AppFuse to use Spring to replace my Factories and Hibernate configuration. It only took me a couple of hours, which says a lot for Spring. I was amazed at how many things just worked. It actually lifted me out of my flu symptoms and made me feel euphoric. Or it could have been the Sudafed. In reality, I only replaced one Factory class (DAOFactory) - a fairly large class that instantiated DAOs using reflection and constructor variable inspection. I was also able to get rid of the ServiceLocator class, the getConnnection() stuff in ActionFilter and the hibernate.cfg.xml file.
The one thing I found when looking at the Petclinic and JPetstore apps was that they used an applicationContext.xml file for unit tests, and a (very similar) one for running the app in a container. To me, this was a warning sign. DRY (Don't Repeat Yourself) is a big reason for using XDoclet and I'm beginning to think that Spring could benefit from a little XDoclet lovin'. Anyway, back to the story.
I wanted to find a way to use the same XML files for testing and in-container execution. As you might know from Part I, AppFuse has 3 different tiers: dao, service and web. To run unit tests for the dao and service layers, I simply load a applicationContext.xml file in my JUnit test's setUp() method and go from there. I saw this in the petclinic app and found that it works pretty well. In the end, I decided to setup different XML files for each layer - applicationContext-hibernate.xml, applicationContext-service.xml and applicationContext.xml for the web layer. The main applicationContext.xml uses entity includes to reference the other two files.
The main pain I found was that the entity includes required different paths for tests vs. running in container. Basically, for tests, I had to use:
<!ENTITY database SYSTEM "applicationContext-database.xml">
While tests, using the ClassPathXmlApplicationContext required:
<!ENTITY database SYSTEM "WEB-INF/applicationContext-database.xml">
Using Ant to do a little replace logic allowed me to jump over this hurdle.
Using this setup, any new DAO definitions are added in src/dao/org/appfuse/persistence/hibernate/applicationContext-hibernate.xml, new Manager definitions (and declarative transaction settings) are be added in /src/service/org/appfuse/service/applicationContext-service.xml. The test-specific applicationContext-database.xml sits in the "test" directory and contains the following:
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location"><value>database.properties</value></property> </bean> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>${hibernate.connection.driver_class}</value> </property> <property name="url"> <value>${hibernate.connection.url}</value> </property> <property name="username"> <value>${hibernate.connection.username}</value> </property> <property name="password"> <value>${hibernate.connection.password}</value> </property> </bean>
While the applicationContext-database.xml for the web is simply:
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean"> <property name="jndiName"><value>jdbc/appfuse</value></property> </bean>
To integrate Spring with my web layer (Struts), I just used the ContextLoaderListener in my web.xml file. I didn't see any point in bringing yet another JAR file into the mix.
<listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>
Finally, to expose Spring's context to my Struts Actions, I added the following to my BaseAction.java class:
private WebApplicationContext ctx = null;
|
This way, the UserManager implementation can be easier retrieved using:
UserManager userMgr = (UserManager) getBean("userManager");
|
The best part about the Spring integration in AppFuse is: (IMO) its Hibernate support and how it drastically simplifies my Hibernate DAOs (as if Hibernate wasn't simple enough already). I dig the ability to specify declarative transactions, and this refactoring seems to have reduced the "src" distribution of AppFuse by 2 MB (to 10MB total)! I don't know where this came from since the Spring JAR is almost 1 MB. The appfuse.war is about 500 KB larger, but I can live with that.
Of course, all of this has been checked into CVS if you'd like to take a look.
Posted by Jason on February 05, 2004 at 09:08 PM MST #
Posted by Patrick on February 05, 2004 at 10:08 PM MST #
Posted by Joe O'Pecko on February 06, 2004 at 12:36 AM MST #
Posted by Juergen Hoeller on February 10, 2004 at 04:16 PM MST #
Posted by Juergen Hoeller on February 10, 2004 at 04:25 PM MST #
There's probably something similar I could do (to specify different files) here, but I like the consistency b/w the <em>webapp</em> and unit tests - even if I do have to do some ant-replacing.
Posted by Matt Raible on February 10, 2004 at 05:52 PM MST #
Posted by Juergen Hoeller on February 11, 2004 at 11:12 AM MST #
Posted by Matt Raible on February 11, 2004 at 02:39 PM MST #
Posted by Unknown on March 12, 2004 at 01:58 AM MST #
Posted by Matt Raible on March 12, 2004 at 02:19 AM MST #
Posted by yecailiu on April 09, 2004 at 08:22 AM MDT #
Try running "ant clean deploy" and see if that fixes your problem.
Posted by Matt Raible on April 09, 2004 at 10:22 AM MDT #
Posted by Shawn on April 10, 2004 at 06:17 AM MDT #
Posted by Matt Raible on April 10, 2004 at 06:17 PM MDT #
Posted by Jeremy Kassis on May 19, 2004 at 11:32 PM MDT #
Posted by Matt Raible on May 19, 2004 at 11:40 PM MDT #
xerces 1.3.x & 1.4.x seem to resolve unspecified, relative paths differently from the new xerces 2.x. 1.3/1.4 use the path that the original file was loaded from as 'root' for resolving relative SYSTEM values, while the 2.x seem to use the JVM root - where the process was kicked.
that's my quickie take on it. not sure if others could describe it differently, but i'm seeing differently SYSTEM values for my EntityResolver impl that are coming into it for resolution.
i would love to hear more about this and if there is a way to configure xerces 2.x to behave similarly to the 1.3.x and 1.4.x versions. i'd like to have my app runnable on both tomcat and weblogic, but wl doesn't like newer versions of xerces for this reason.
http://edocs.bea.com/wls/docs81/xml/xml_admin.html
yikes!
c
Posted by chris butler on June 14, 2004 at 08:36 PM MDT #