Hello Johannes,
Thanks a lot for your prompt and detailed reply.
Hereby, I'm going to comment some of your remarks:
> Hi Fabrizio,
>
> I wrote most of the tutorial you cite two years ago, but from the
> discussions on the list got the impression that most people switched to
> the CHS (Cocoon, Hibernate, Swing). Thus it is interesting to see that
> someone is actually still using this technique. I do it still, as well,
> since it "works for me" for medium-sized projects. So let me comment on
> the issues you bring up:
And it still "works for me" too :-)
To a Cocoon/Hibernate newbie, or to anyone who doesn't want to introduce
yet another framework (Spring), your tutorial not only gives a pragmatic
solution for integrating Hibernate into Cocoon, it also gives a lot of
insights into the workings of Cocoon & Avalon!
>> However, for more complex scenarios which involve displaying a sequence of
>> forms to the user, this 'session-per-request' approach is cumbersome - as
>> whenever the Flowscript continuation is suspended/resumed, you have to
>> manually re-associate any detached objects to the new Hibernate
>> session...
>>
> This is true, and sometimes unconvenient, e.g. when you have a complex
> form spanning multiple pages.
>> So I've taken a closer look on the long session / conversation approach
>> described by the Hibernate authors - although it is generally considered
>> an anti-pattern by most web frameworks, due to its wasteful memory
>> requirements and potential garbage collection issues.
>>
> This is not the only problem really, but I go into this in more detail
> below.
>> Correct me if I'm wrong, but all the objects which I fetch before a
>> continuation is suspended (and which I don't explicitly care to dispose
>> of!) just stay there, hogging memory ...until the user decides to resume
>> the continuation or until the continuation times out, right?
>>
> Exactly.
>> But is my reasoning above, that a 'Flowscript continuation is indeed a
>> long session', correct, or did I overlook something obvious?
>>
> This is precisely what flowscript continuations are doing. However,
> there is a tricky philosophical issue here.
You've got me reassured here! To me, flowscript continuations used to
carry a 'black magic' connotation for quite some time.
> ... A Flowscript connection
> holds exactly the information that is necessary to continue program
> execution at the point it was interrupted by e.g.
> cocoon.sendPageAndWait(). This usually boils down to local and global
> variables. In fact, if you have a global variable in your flowscript,
> Cocoon will automatically create an HTTP session to store it in, even if
> its value is never accessed or modified. Otherwise, an HTTP session will
> only be created when you call sendPageAndWait() or something similar.
>
> Of course, the global variables you are storing can also be "real" Java
> objects instead of trivial javascript variables containing just integers
> and strings. I used this, for example, in an online shop application
> where the shopping basket is a java.util.Vector containing objects which
> were initially fetched by Hibernate. This is when I first ran into the
> problem you described: After the first continuation, Hibernate would
> complain about accessing an object whose session had been closed. I
> actually solved this problem by copying the fetched objects to
> non-persistent instances.
>
> In theory, it is of course also possible to store the Hibernate session
> itself in the HTTP session, or as a global variable in the flowscript.
> However, there are several reasons why I think this is a bad idea:
>
> (1) Connection pooling. In my setup - which is also described in the
> wiki - Hibernate uses pooled connections. If a lot of sessions are
> opened and not closed properly, or by a timeout, I am not sure whether
> the underlying JDBC connection would be properly closed and returned to
> the pool.
I tend to be more pragmatic, rather than philosophical ;-)
Therefore I ran various stress-tests using the 'ab' tool ('apache
benchmark', included in the httpd distribution), in which several
thousand requests (with configurable concurrency levels) are launched
against a Cocoon/Hibernate webapp.
During simulations of denial-of-service attacks it proved to be robust
(slowdowns during high loads, but no hangs) and its memory requirements
were stable (no memory leaks!)
Your 'HibernateFilter' never missed a Hibernate session - the JDBC
connections were always properly returned to the pool (I could
observe live which ones were being used using the Oracle 'TopSessions'
tool)
As a side-effect I found out that a Cocoon pool of only 8 JDBC
connections, was able to handle any reasonable load occuring in
our organization (ca. 1500 users) but that's another story...
> In any case, you will run out of connections faster than when
> returning each JDBC connection to the pool when the view was rendered.
> Actually, it is a classical and frustrating problem to run out of JDBC
> connections, and it is one of the main strengths of the
> OpenSessionInView design patterns that it gets rid of this issue.
Yes, that was also a result of the ab-tests!
As a side-note, before taking the Cocoon/Hibernate route, my initial
Cocoon database tests were performed using the SQL-Transformer, and yes,
it quickly ran out of JDBC connections and into unrecoverable deadlocks.
(Note that that was in Cocoon 2.1.7 - the deadlock issue has apparently
been solved in later versions of the SQL Transformer)
> (2) I think that it is against the philosophy of flowscript
> continuations to store objects which have side non-obvious side effects,
> such as keeping a JDBC connection open or needing an open Hibernate
> session to call the setter methods, across sessions.
For the long-session approach, I've modified the HibernateFilter so that
it will always disconnect the underlying JDBC connection from the
Hibernate session, thus returning it to Cocoon's connection pool, but
the Hibernate session is left open (except when a 'end-of-conversation'
flag is being set)
The Hibernate session is just an additional object that is being stored
as a local variable, along with the other objects in the continuation.
The only side-effect that comes to mind when keeping open (but
disconnected) Hibernate Sessions around, is that they won't be
automatically cleaned up when the continuation expires (I found that
out using SessionFactory.getStatistics())
I've modified the 'HibernateFactory' to keep track of every session it
creates, and to regularly cleanup (close) Hibernate sessions that
belong to expired continuations.
> (3) Most "write" operations should be wrapped in a JTA transaction to
> make sure that they are actually executed. Since these transactions are
> atomary, this might block other JTA transactions for a much too long
> time (although I am not 100% sure on this one)
I've never delved into the Java Transaction API ...I probably will when
I stumble upon a distributed transaction issue :-/
> (4) Architecture. I never felt good about using Hibernate directly in
> flowscript anyway, since it is supposed to be on the "M" (model) layer
> of the MVC pattern, while flowscript is IMHO on the C (control) layer.
> With the current setup in wiki, sessions are opened in flowscript, but
> closed in Java, which is ugly.
Yes! That's also one of my only gripes!
I've somewhat limited the impact of this Flowscript/Hibernate code mix
by encapsulating the Hibernate session setup in a generic dispatch method
(invoked by the sitemap) and by using transaction defaults which apply
to most (over 90%) cases.
Thus, the only flowscript methods where you will see explicit transaction
demarcation statements are those dealing with CForms.
I had briefly considered Spring, using its AOP features to perform
transaction demarcation. But I dismissed it, as that would require
a major code rewrite, and introduce a new complexity layer.
Cosmetics alone just ain't worth it in this case.
> I think that the way to go is the
> classical "DAO" (data access object) pattern, which should wrap calls to
> Hibernate - which brings you closer to what Spring is supposed to do,
> but of course you can implement DAO yourself without using Spring, which
> is what I do. In this setup, Hibernate sessions are openened and closed
> as needed in the Data Access Objects. In flowscript, you only interact
> with the data access objects. This makes it necessary to provide
> Hibernate with a different connection pool than Cocoon's, but that makes
> perfect sense to me since it decouples cocoon from all the database and
> hibernate stuff, thus leading to a "clean" MVC structure (apart from the
> OpenSessionInView filter, but this is a standard issue).
Opening and closing sessions in DAOs?
Hmm, here I have to agree with some philosophers out there that do not
quite agree (see http://www.hibernate.org/328.html)
The bit that troubles me is that you lose transaction atomicity when
changes to multiple DAOs have to be performed in the same transaction...
> I hope to have enough time to document this setup in wiki soon, since I
> like it much more than the described one. However, I would also be glad
> to hear more from your experiences with the open session across
> continuations - it is certainly (to me) one of the more interesting
> aspects of Java web development that you have a variety of philosophies
> and approaches.
"There's more than one way to do it" - that used to be a slogan for the
Perl language, and it certainly applies here as well.
> Apologies for the lengthy reply and best regards,
> Johannes
Apologies? You're kidding!
I should thank you for the time you've invested in this reply!
Fabrizio
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]