I took a bit of a look at it and it looks fine, thanks for providing
this example. It's a little hard to compare since it doesn't actually
run properly, but I was able to get the basic idea. As for my general
thoughts on Spring vs. Guice ...
From what I have seen from Spring and Guice so far I would basically
agree with what this article says ...
http://code.google.com/p/google-guice/wiki/SpringComparison
Guice seems to be the smaller, lighter, and more focused DI option and I
strongly prefer their Module approach and their use of annotations over
config files. The performance stuff sounds interesting as well although
I don't consider it a huge issue. The benefit of Spring of course is
that it offers more options in terms of non-DI stuff such as the mail
sender and thread executor stuff.
As for some of my thoughts on what I looked at in your code ...
* I definitely hate all the config files. I don't know if you separated
them for example purposes, but you have like 7 config files and IMO that
is way confusing. I would really only consider Spring if this was all
merged into a single file or at least just one or two files.
* The way you build and configure the mail and db providers is
interesting and could be useful, but I don't think it's a significant
improvement over what we already have.
* The property file injection thing is interesting and seems powerful,
but i'm worried it's not entirely appropriate for us. for one, it means
that there are now 2 ways to get properties out of those config files
(via Spring and RollerConfig) and that complicates things. second, lots
of the the stuff in those configs doesn't apply to the backend, so it
seems weird to support duplicated config mechanisms for the backend
wired by Spring and for the rest of the code. In your example you
didn't include the fact that a 3rd way to specify roller properties is
by supplying a path to a file via a jvm option, so you would need to
support that as well.
* The way you did the persistence stuff is probably what i liked least.
I am not in favor of wiring up our backend in a Spring specific way
using things like Springs HibernateUtils, etc. I already think that ORM
solutions provide enough of a challenge in terms of figuring out exactly
how a call to save an object turns into actual jdbc actions and tracing
that process becomes much more complicated when you mix in yet another
dependency like Spring. So I am not a fan of introducing Spring in this
area and what we have now works fine so there is no reason to change it.
All that being said, this stuff has nothing to do with using Spring DI
for the backend and is not required as far as I know so it's just
something I would cut out.
* You suggested getting rid of the XXXRollerImpl classes and just using
a generic RollerImpl class but that sounds like a bad idea in my mind.
The purpose of having those classes is to force a persistence strategy
as a whole meaning that a JPARollerImpl should only allow injection of
JPAXXXManagerImpl classes. From an api point of view it would be very
silly to be mixing JPA and Hibernate code together at runtime. Like the
last point, I think this is not something that really has a bearing on
whether or not Spring DI could be used, but it's something that I am not
a fan of in your particular design. Also related to this one is that I
am 100% against allowing for public setter methods in the Roller
interface or it's implementations. I mentioned this before, but the
Roller instance should be immutable as far as the api goes and I think
we need to enforce that when we do this DI work.
* The thread manager stuff is probably the most interesting piece in my
mind and I wouldn't mind making use of Spring's built-in executor
service stuff. The problem though is that we don't really have much
need to do this 'cuz our code is fine as it is, and the other problem is
that translating our config file properties into Spring has proven
challenging with the Acegi stuff and I am hesitant to try.
So overall my basic feeling is that Guice provides the nicer option
specifically for DI and I love that it's all done via java code and
annotations and that the way you define Modules provides some compile
time checking. Spring would work fine for DI, but I am still just luke
warm on the config file wiring, so if I was making a decision now I
would go with Guice.
Thanks again to Denis for providing the Spring example, it's much easier
to do a comparison when we can see both options :)
-- Allen
Denis Balazuc wrote:
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