mercoledì 12 dicembre 2007

Seam Spring and connections lost

Using high level frameworks as seam and spring, you think you are safe from dangerous things like loosing open connections to your db .... but it happened.
I don't know why yet, but using spring transactions with seam and an hibernate transaction manager my webapp was eating connections, expecially when starting (and not ending) long running conversations. Reload many times the page with @Begin and my webapp was lost.
My solution was to switch to JTA, and it was really simple when:

1) found the right hint at http://archives.free.net.ph/message/20071121.214555.86512d04.en.html to choose the right JTA implementation for my postgresDB (that for now is not in the supported list of BTM, I hope for the better ;-) );
2) read BTM article to configure it in spring;
3) downloaded latest jdbc driver for postgres, with a must have fix.

IMPORTANT:
you have to configure hibernate.connection.release_mode to after_statement (with JTA) beacause Spring changes default to on_close, and long running conversations keep the hibernate session open (and hopefully disconnected from db connection).
Moreover, as written in Seam docs, set hibernate.transaction.flush_before_completion to true, so to flush the session before commit of surrounding transaction.

<!-- I have this in my hibernate.cfg.xml -->
<property name="hibernate.connection.release_mode">after_statement</property>
<property name="hibernate.transaction.flush_before_completion">true</property>



OTHER NEEDED CONFIGURATIONS:
---------------------------------------------------------------
Spring's application.xml:

<bean id="dataSource" class="bitronix.tm.resource.jdbc.PoolingDataSource" init-method="init" destroy-method="close">
<property name="className" value="org.postgresql.xa.PGXADataSource" />
<property name="uniqueName" value="pgsql" />
<property name="minPoolSize" value="0" />
<property name="maxPoolSize" value="${db.maxActive}" />
<property name="driverProperties">
<props>
<prop key="databaseName">ibusiness_web</prop>
<prop key="user">${db.user}</prop>
<prop key="password">${db.pwd}</prop>
</props>
</property>
</bean>

<!-- Bitronix Transaction Manager embedded configuration -->
<bean id="btmConfig" factory-method="getConfiguration" class="bitronix.tm.TransactionManagerServices">
<property name="serverId" value="spring-btm" />
</bean>

<!-- create BTM transaction manager -->
<bean id="BitronixTransactionManager" factory-method="getTransactionManager"
class="bitronix.tm.TransactionManagerServices" depends-on="btmConfig,dataSource" destroy-method="shutdown" />


<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource">
<ref local="dataSource" />
</property>
<property name="configLocation" value="/WEB-INF/classes/hibernate.cfg.xml" />
</bean>

<bean id="seamSessionFactory" class="org.jboss.seam.ioc.spring.SeamManagedSessionFactoryBean">
<property name="sessionName" value="hibernateSession"/>
</bean>

<bean id="hibernateTemplate" class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory">
<ref bean="seamSessionFactory" />
</property>
</bean>


<!-- Spring JtaTransactionManager -->
<bean id="transactionManager" class="org.springframework.transaction.jta.JtaTransactionManager">
<property name="transactionManager" ref="BitronixTransactionManager" />
<property name="userTransaction" ref="BitronixTransactionManager" />
</bean>


-----------------------------
Seam's components.xml:


<persistence:managed-hibernate-session name="hibernateSession" auto-create="true" session-factory="#{sessionFactory}"/>
<spring:spring-transaction platform-transaction-manager="#{transactionManager}"/>
<spring:context-loader config-locations="/WEB-INF/application.xml"/>




---------------------------------------------------------------
NOTE: in application.xml use as reference for other beans seamSessionFactory as the only sessionFactory. sessionFactory is defined in Spring only to be wrapped by Seam in components.xml.
---------------------------------------------------------------

Thanks to spring, I didn't change a line of code, and very few of xml.
Thanks to seam, now I can leverage its long running conversations to improve my app.

Thanks to google and all of you who share their good and bad experiences, so I do now.

Seam and asynchronous email send

Over the past 3 months I've ported a JSF 1.1 business application written last year to Seam.
I'll talk of the hurdles I met, expecially porting from myfaces 1.1 to JSF RI 1.2, but now I just want to share with you my few successes.
In seam you can send an email using facelets as template engine. You can do it in same thread that displays you page, but due to high latency of smtp servers and other reasons, it's better to send it asynchronously.
My webapp is a POJO webapp, built upon spring,hibernate and seam.
All you have to do to spawn a new thread is to annotate your method with @Asynchronous, but watch out !
If your code was already working when doing it inside the display thread, now it surely will break.
WHY:
1) when seam spawns a thread, you loose the previous event/page/conversation context, so you have to put the objects you use inside the email template in a new context:

@Asynchronous
public void send(Account account) {
try {
Contexts.getConversationContext().set("account", account);
Renderer.instance().render("/email/activatedaccount.jsf");
} catch (Exception e) {
e.printStackTrace();
}

2) you loose your webapp relative paths too: if inside a display thread seam uses the ServletContext to retrieve the full path to your email template (and the resources it uses). Inside a spawned thread seam replaces the servletContext with a mockContext which resamble a ResourceBundle strategy to find resources in the classpath. So you have to move /email dir in WEB-INF/classes, togheter with the resources it uses (logo images etc..).

3) I happened to use one backing bean to process the page action, and call a send(account) method of the same class. When I marked that method @Asynchronous and debugged inside Eclipse, I saw the thread was the tomcat's one, not a new spawned one. I solved it moving the send method in another class and calling it in a new istance:


public String approve(Account account) {
...
architectMailer.send(account);
...
}

4) There's a bug (fixed in next release, perhaps already in 2.0.1 CR of tonight) about inline images not being displayed in thunderbird, but only attached. Don't go crazy, it's not your fault: wait for next release.

My 2 cents

Hope this helps