Hi all !
Anyone had time to have a quick look at this?
I'd love to get some feedback, see if I should carry on in that
direction (I'd be more than happy to!)
Thanks a lot
Denis Balazuc
Denis Balazuc wrote:
Hello all
I've finally found some time to tidy up a Spring demo for Roller. I've
tried to include various different aspects of what Spring can bring into
a project such as Roller (depency injection, interceptors and
transaction management mostly) and focused on easy back-end switch and
simple configuration for the mail and the main datasource. There are
many areas that I didnt address but where I can see some benefits from it.
#Journey onward with Spring!
Before going further, I would like to mention that, with ACEGI already
at the source of the security model of the Roller webapp, it was easy to
convince myself that introducing Spring in the business layer was just a
natural continuation of the work that's already been done there and a
good goal to pursue. Besides only addressing the back-end, there is a
lot that can be done on the webapp side too. So...Spring's already
there, temperature's getting good....let's go!
A few first things
* File(s) URL are at the end of this mail. For the impatients, it's
better to read from the bottom to the top.
* When mentionning classnames or other code details, I will be referring
to version 541334 of the trunk, which is before refactoring of the pojos
and right in the middle of the creation of the /component directory.
Classnames and details discussed hereunder may be slighty out of date. I
also did not consider Planet as I reckoned that most of what is involved
applies to Planet the exact same way, and RollerConfig (which should be
trivial to Springify) to avoid getting in the midst of
too-much-changes-at-once.
* version 541334 has a few problems with creating new entries or categories
If you ever happen to want to try to run my build, expect a few things
to not work correctly, including things I've probably destroyed along
the way. Having everything pristine is not the goal of this demo and
code has moved since so it really did not really matter.
* I haven't tested all of everything, some bits hereunder are also
theory only, but those are not rocket science to implement despite my
hidden kid desire to be a rocket scientist.
#Mail and DataSource
First thing, I wanted to address the problem of switching the mail
session and the datasource, from JNDI to plain good auld java code or
others. Spring allows you to switch those implementations without code
that will fetch a property, check which of JNDI or plain mail session
(or jdbc datasource) to use, and so forth. Surely it requires injecting
a bean or some lookup code. For that, I have modified MailUtil.java and
re-implemented DataProvider.java to use a Spring bean. In the case of
MailUtil.java, a Spring interface, JavaMailSender is being used
internally in place of a javax.mail.Session instance. In the case of
DataProvider.java, Spring actually makes it obsolete as it can provide a
datasource bean from configuration so its implementation looks up for a
datasource bean.
An interesting note is that the roller(-custom).property switch
(depending on the "database.configurationType" property) is implemented
within the Spring configuration, removing the need for the various
service providers or factories within the code.
This also shows how it's possible to move a lot of configuration logic
out of the code and into XML files that usually never change.
See
src/applicationContext-rollerMail.xml
src/applicationContext-rollerDatasource.xml
#"Back-end" implementation
I got caught in the middle of JPA changes while doing this work,
so....switching back-end sounded like a good target for dependency
injection. We have interfaces, we have implementation classes, ideal
conditions for a lift-off!
RollerImpl.java has been modified and merged from
HibernateRollerImpl.java so it contains ALL the Roller manager
implementations. Those are injected through
src/org/apache/roller/business/applicationContext-business.xml using
either common beans for the managers that do not have links to the
database, either back-end implementation specific beans (Hibernate, JPA,
all you can invent). Of course, the singleton nature of those
implementations is preserved where required.
See
src/org/apache/roller/business/applicationContext-business-hibernate.xml
This allows for
-- Switching back-end implementations such as Hibernate, JPA or unit
test mocks/stubs easily.
-- Adding declarative transaction management and remove the need for the
code to worry about that aspect which in Roller impersonates as the
"HibernatePersistenceStrategy". In that particular matter, there would
be a lot to discuss about the current implementation of the Hibernate
layer using the HibernatePersistenceStrategy and the different approach
that is, somewhat unfortunately, required to take when going the Spring
way. For instance, the usage of Spring's HibernateTemplate or
HibernateCallback(s) are encouraged, effectively tying the DAO code to
Spring, but also moving the transaction headaches to configuration. As a
developer, I tend to favor the fact that I do not have to worry about
transactions or such horrible things, and leave those headaches to those
who integrate with various systems. Being able to do it by configuration
is a good plus.
-- Ungluing the implementation code and allowing the removal of
HibernateRollerImpl and friends, since each single manager is a
different Spring bean, maybe sharing common attributes (such as the
HibernatePersistenceStrategy). Those beans are injected in the
RollerImpl facade, which allows for easy switching when, for example,
unit test comes in the loop.
-- In the very interesting case of the ThreadManager, and more generally
the scheduling of tasks, my first thought went about what's going to
happen to application managed thread pools or schedulers in a strict
J2EE environment. If you intend to run Roller in a J2EE 1.4 containers,
you may want to use container-managed thread pools, or WorkManager(s) or
other Websphere-like WorkArea(s) and Scheduler(s). Since you can,
transparently to the code, lookup an EJB or provide a home-grown
implementation of a ThreadFactory/ExecutorService/etc, Spring is very
much welcomed here. As an example. the switch from ThreadFactory to
ExecutionService(s) in the branches after the one I was working one can
be performed from configuration, with a small changes in the
ThreadManagerImpl code to allow injection.
#Property loading
As mentionned above, Spring makes it easy to load various property
sources. Spring configuration files may also reference property values
using the usual ${value} syntax, which is what is used to populate
properties among the various manager beans and, among many, provide the
datasource parameters depending on the roller.properties and
roller-custom.properties. Again, RollerConfig wasn't taken into
consideration but it's easy to adapt its implementation to fetch its
properties from Spring.
see src/applicationContext-rollerProperties.xml
#Conclusion
I'm not great a concluding so here's some more concrete material
#Files and reference
I've packed some demo code within the following files. I decided against
providing a really functional patch to submit to JIRA since my code
baseline is already old compared to the recent changes for 4.0 and this
whole thing is just a proof of concept.
*
http://denis.balazuc.net/roller/roller_541344-spring-stable.zip
(1) That's the Roller code my build is based on (from version 541344 of
the trunk). Much too big (150Mb) to download and messed up since it's my
working copy, but well...
*
http://denis.balazuc.net/roller/roller_541344-spring-stable-webapp.zip
My current test web app only (Roller - no Planet) with all JAR files but
exploded roller-*.jar for reference (25Mb)
*
http://denis.balazuc.net/roller/roller_541344-spring-stable-webapp-nojars.zip
My current test web app only (Roller - no Planet) without any JAR file
and exploded roller-*.jar for reference (6.7 Mb)
*
http://denis.balazuc.net/roller/roller-trunk-541344-spring-patch.txt
Patch made from (1) from trunk version #541344. You will still need the
JAR files listed above. I"m not very familiar with patching so it might
not be reliable.
First things you may want to look at are the Spring configuration files
(it's not XML hell, honest)
/src/applicationContext-rollerProperties.xml
/src/applicationContext-rollerMail.xml (used in MailUtil.java)
/src/applicationContext-rollerDatasource.xml
/src/applicationContext-roller.xml
/src/org/apache/roller/business/applicationContext-business.xml
/src/org/apache/roller/business/applicationContext-business-hibernate.xml
#Build considerations
I had to replace a few JAR files with new ones and created
/tools/spring-2.0.5
/tools/spring-2.0.5/acegi-security-1.0.3.jar
/tools/spring-2.0.5/ehcache-1.2.4.jar
/tools/spring-2.0.5/spring.jar
#More files for the curious
* There is a
/src/org/apache/roller/business/applicationContext-business-hibernate2.xml
along with its own hibernate2 implementation package, which uses a
Hibernate SessionFactory provided by Spring and delegating transaction
management to the configuration, rather than implementing a specific
"strategy". This setting is not currently working as it would require a
different approach on DAOs. The provided example shows how transaction
management could be done, but ideally, Spring's HibernateTemplate and
HibernateCallback(s) should be used to perform simple unit of (database)
work within a transaction into the Hibernate implementation layer if
Spring is to be exploited with its full potential.
* The file
/src/org/apache/roller/business/applicationContext-business-jpa.xml is
not functional at all but exists for the sole purpose of demonstrating
how painlessly implementations can be switched if required.
**
http://denis.balazuc.net/roller/blug-project-demo.zip
This is a personal project I had started after Struts-ifying Pebble
(http://pebble.sourceforge.net/), which still serves my wife's blog
nicely but needs replacement, and before meeting Roller. Most of the
Spring ideas exposed here come from there. I've tried to separate the
DAO and business layers in various projects, which helps leveraging
Spring's benefits.
Cheers
Denis Balazuc