You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@roller.apache.org by Denis Balazuc <de...@balazuc.net> on 2007/06/03 05:38:04 UTC
Spring Roller
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
Re: Spring Roller
Posted by Anil Gangolli <an...@busybuddha.org>.
One of the valuable aspects I have found in adopting DI is that it
encourages modularity and opening up the component model in such a way that
extension and replacement of implementations in all areas of the code just
becomes more natural and easier.
I'm interested in seeing us adopt enough of these practices and patterns
that we get those benefits, whether with Guice or Spring.
I'm curious how the use of annotations provide for this kind of
extensibility / pluggability without the need to rebuild, without limiting
what can actually be plugged in or replaced without recoding existing code,
and without the need to code our own configuration handling code. These
are things I know how to get with Spring and XML-based configuration. I
think if we can get them with Guice, I'll be ok with it.
--a.
----- Original Message -----
From: "Dave" <sn...@gmail.com>
To: <de...@roller.apache.org>
Sent: Friday, June 08, 2007 7:00 AM
Subject: Re: Spring Roller
> On 6/7/07, Denis Balazuc <de...@balazuc.net> wrote:
>> 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!)
>
> I haven't had a chance yet, but I will once I'm ready for Roller Guice
> show and tell -- I'm getting very close to that point now.
>
> - Dave
>
Re: Spring Roller
Posted by Dave <sn...@gmail.com>.
On 6/7/07, Denis Balazuc <de...@balazuc.net> wrote:
> 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!)
I haven't had a chance yet, but I will once I'm ready for Roller Guice
show and tell -- I'm getting very close to that point now.
- Dave
Re: Spring Roller
Posted by Denis Balazuc <de...@balazuc.net>.
Hi Allen
Thanks a lot for the feedback!
Allow me some more comments and attempts at being a little clearer :-)
> 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.
Sorry about that. I jumped in the middle of some major code changes for
4.0 so I had troubles getting something stable to run, but yup, that
wasn't really the goal.
> As for my general
> thoughts on Spring vs. Guice ...
>
> 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.
>
Yes, I've tried to show in my sample, there's a lot more in Spring than DI.
Generally speaking and performance-wise, the only hit with Spring is at
application startup, so it's of very little concern in a web app world,
or systems such as Roller.
Actually, I've heard a lot of claims about Spring slowliness that have
been proven wrong with my past experiences, and honestly, the benchmark
code
(http://google-guice.googlecode.com/svn/trunk/test/com/google/inject/PerformanceComparison.java)
looks a little silly to me: it goes down in the guts of the Spring API
that no-one ever touches to benchmark some accesses or construction
patterns that the Spring factories implementation classes optimize,
bypassing that totally, which is the whole point of Spring providing
such a vast API and set of objects. There are many areas where
performance actually improves by using Spring since you can for example
lazy-load any bean you choose and don't have to manage any synchronized
access (or very few on implementation specific classes) or
caching-related things in the code.
I don't have exact figures for Roller but I noticed a 12s delay vs 8s in
Tomcat context loading using Spring.
Past that point, without using lazy-loading, I couldn't see any
difference, even when adding interceptors to log DAO or business
methods. You can "feel" beans being lazy-loaded (1/2s at worse?) when
switching that on but again, that doesn't matter since it's done once.
> * 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 mail and database provider are in separated files to make sure that
modules that would only use the mail provider or database provider don't
load any unwanted/unnecessary bean by using only one file (a common
mistake with using Spring without lazy-loading, which I did when I
started playing with the Roller code for mail util).
Now for all the rest (all the XML for the implementation beans), it
really is just me and my coding style (I'm a "500 lines of functional
code in a single java class is probably more than you'll ever need" kind
of refactoring ayatohlah). It's very easy to have only 3 Spring files,
or even only 1. However, separating the business impl also helped me
switch from one back-end to another in one line while
testing/experimenting.
Using config files is indeed a different approach than using
annotations. I am personnally a fan of XML and I much prefer
configuration files to instrumenting code with annotations or other
comment tags that are tool-specific, requiring for example a build cycle
to fix a setting (such as the hibernate tags). I also tend to dislike
binding business objects to anything else than the API they belong to.
However, that's a very philosophical debate that we'll keep for another
time.
> * 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.
As I mentionned in my previous mail, I didn't went as far as I wanted to
with property files.
Of course, RollerConfig and RollerRuntimeConfig should use the same
mechanism and sources,
which can easily be done by modifying their implementation. Ideally,
there should be few to no references to static methods in a code that is
using DI as its core (each property a class needs should be injected, so
as to help testing and re-use) but we can get around easily with that
since RollerConfig is pretty much everywhere. I didn't provided a
RollerConfig implementation that reads its configuration from Spring for
lack of time, and fear to break too much stuff in the same batch.
> 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.
I wasn't aware of that option, but that should be a breeze to add. That
won't probably even require writing any code.
> * 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.
Indeed, there is no reason to change the existing and the "hibernate2"
package is a scary, ugly and bad attempt at showing how transaction can
be moved to yet-more-XML-headaches and replace home-grown "strategies".
Ideally, it is best to use Spring's HibernateCallback(s) and
HibernateTemplate, which is one area where you want to tie an
implementation layer with Spring. But with those, you usually never have
to manage any transaction aspect, unless you do very complicated and/or
unlawful things with your SQL/Hibernate code, or need to support
transactions other multiple datasources. It also allows for using JTA by
just switching it on.
The business impl layer of this project
http://denis.balazuc.net/roller/blug-project-demo.zip
is probably a much better example of a simple DAO layer using Spring
with Hibernate.
Basically, HibernateXXXImpl (or HibernateRollerImpl - see below) can be
taken as is, have its strategy configured and injected through Spring,
and off we go.
> * 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.
You could go either or both ways actually.
Injecting a XXXRollerImpl in RollerFactory, or get rid all along of the
XXXRollerImpl really is a design-decision. My only reason was to remove
classes that do some wiring by code, managing their own singletons to
show how it gets simplified. It surely makes little sense in the
existing implementation. However, using Spring, it's a matter of
defining a "prototype" (or "abstract" or "template") bean and moving
configuration around, as long as you have the proper setters and getters.
> 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.
>
I could not agree more that setters have nothing to do in the Roller
interface. However, If you're using setter injection, you need public
setter (getters aren't necessary if not part of the implemented
interface) in interface implementors. I am usually against public
setters when it's not needed, but once you have DI in mind while
designing/coding, there's rarely any misuse of it. You can opt for
constructor injection, but unless the whole design is 100%
interface-driven, it makes it harder to integrate interceptors, use
dynamic proxies or reflection without a default constructor.
When I started working using DI, having public setters and getters used
to itch me a lot (maybe less than protected member variables though),
but since the "javabean-like" approach gets on everything, from Struts
action forms to Spring beans, it's now became the usual pattern and at
the end of the day is much cleaner than using protected members which
access can't be traced to avoid getters/setters (even protected), and
allows for a lot more fun such as mocks/stubs/etc.
> * 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.
The ExecutorService is actually a java.util.concurrent implementation
that got switched in Roller between two updates I did of the code IIRC.
All I did is add a constructor to inject the service, as I did for all
other XXXimpl, comment out my current Spring conf, switch it to
injecting an ExecutorService using the parameters I gathered - that's
all....Yup, that easy - and that was fun to do! :-)
It's actually a good example where Spring can really make a difference
with its possibilites. You can inject anything from a flat static
implementation of an interface, to an EJB, and this totally
transparently at a switch of a config. With Spring's use of interceptors
and dynamic proxies, you can go extremely far.
I'm currently working on a well-known brand of J2EE 1.4 app servers IRL
and with current threadmanager and scheduling implementation, I am not
sure I would not get barked at and asked to use a WorkManager if I were
to run Roller tasks on them. Being able to not touch the Roller code
(maybe provide my own external package) and change one bean using a
config file to make use of those server's built-in TaskScheduler(s)
would be a very good thing.
Aside from the rich set of services that Spring offers, one of my main
argument in favor of Spring for Roller is actually that it's already
used in Roller with Acegi, which makes the config changes in the webapps
and build files quite minimal. Also Struts/Struts2 modules already could
benefit from it: actions that want mail senders, or filters, or
renderers, all this may be injected through Spring extremely easily with
the Struts plugin. I was actually surprised that it wasn't already that
way and that they were still hard-wired dependencies on various services.
As for translating configuration files, I'm looking for something I
could help on, show me where to look ;-)
It is true that the learning curve with the Spring config is steep at
first but after the getting-used-to phase, it's easy to navigate between
the Spring API and the XML to change pretty much any aspect of an
application at a glance.
> 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.
>
As I mentionned earlier, I much prefer configuration files to
instrumenting code with annotations but it all boils down to design
decisions. Spring makes sense since it's already in Roller and allows
for all those neat services and helpers that it comes with on top of the
DI way that Roller is taking. It's worth the XML headaches IMO (I can't
legally ship aspirin from here...). In the end, a good Spring
deployement should hide the complexity of its configuration to end-users
or users of the API but may go as far as one wants in decoupling code.
If you want more Spring magic to be shown (I haven't covered Struts2
yet!) or someone to do any config macarena you hate, I'd be happy to do
more. If you give the go on Guice I'll just read more on and get my
hands deeper on it, but in both cases I'd love to help.
Thanks again for the great feedback.
Denis Balazuc
"A good Spring implementation is indistinguishable from magic."
No...Wait...That doesn't sound right...
Allen Gilliland wrote:
> 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
>
Re: Spring Roller
Posted by Allen Gilliland <al...@sun.com>.
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
>>
Re: Spring Roller
Posted by Denis Balazuc <de...@balazuc.net>.
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
>