Spring-faces provides great integration between Spring Web Flow 2 and JSF. Each view-state in web flow renders a JSF view, a stateful tree of JSF components. When exiting a view-state (e.g. when transitioning from one view-state to another) the state of the JSF component tree is naturally lost. There are times though when you want to transition back to that view-state and see the view exactly the way it was last time. For example you might want to transition to a view-state where you search for and select a value and then you want to go back to the originating view-state and see it exactly the way you left it.
A nice way to achieve this with spring-faces is to store the JSF component tree state in the flow scope when leaving the view-state. Then, when entering it again, you may restore it and clean the flow scope. The best place to put this logic is in a FlowExecutionListener. Saving the JSF view state must happen before the transition and restoring must happen on entry of the view-state.
The fundamental questions are:
1) where can you find the JSF state to save?
2) where should you put the JSF state when you restore?
Well, the answer to both questions is the same. When you restore the JSF state you need to put it at the same place that you find it when saving it. That is in the view scope under the key "flowSerializedViewState". So right before the transition you need to copy the contents of viewState.flowSerializedViewState to flowScope.myKey and on entry of the view-state you need to copy the contents of flowScope.myKey to viewState.flowSerializedViewState. Web flow's JSF StateManager will actually restore the JSF component tree using the state you injected into the view scope.
That's it. Remember that this is actually a hack of the Spring-faces state manager, so it comes with no guarantees of later versions compatibility. But it's a neat way to save and restore the JSF view state accross Spring web flow view-states. This works with the current latest version of SWF (2.0.7).
Κυριακή, 26 Απριλίου 2009
Παρασκευή, 02 Μαΐου 2008
Spring Web Flow 2.0.0 released
Spring Web Flow 2.0.0 production release was out 3 days ago. This one is quite of a milestone release. From my point of view, the most important change in this release is the fact that SWF now includes the responsibility of view rendering instead of pushing off this task to its caller (usually Spring MVC). See the vision of SWF 2 here. This important change enables a wealth of new features, such as most importantly the introduction of a new module called spring-faces.
Many other interesting features are there too:
Many other interesting features are there too:
- New simplified flow definition syntax,
- Spring Javascript module introducing support for Ajax views,
- A library of JSF components (part of spring-faces),
- FormAction is replaced by declarative binding and validation to model
- Popup view-states,
- Flow managed persistence contexts (session-per-conversation)
- Flow definition inheritance,
- View scope,
- History polices,
- Integration with security,
- etc
Τρίτη, 29 Απριλίου 2008
Integrating CMT EJB, Spring, and Hibernate
/* using Spring 2.0.8, Hibernate 3.2.5 */
Integrating Spring and Hibernate is quite well documented in the Spring reference and other books. However, there is a special case which involves an EJB with Container Managed Transactions (CMT) --> calling Spring --> calling Hibernate. This has been a little confusing for me.
So what is the case? Suppose we have a Message Driven Bean (MDB) and we want to do some work with Hibernate upon reception of a message. The container dequeues the message under the covers before control is handed over to our MDB. We want this dequeue operation to be part of the transaction. Using CMT is the most straightforward configuration that comes to mind. Another option, which achieves the same results but without including the dequeue operation in the actual JTA transaction is described in this article.
Since we am using CMT, we are not going to use any transaction management at the Spring level (PlatformTransactionManager). We are letting the container do all transaction demarcation. Why is that? According to the J2EE spec, CMT beans are not supposed to interact directly with JTA (not event through UserTransaction). This applies to Spring's JtaTransactionManager within CMT EJB. Although this actually works in many application servers, it does violate the spec. Therefore, in this scenario we are not going to use Spring transaction management.
At this point, I have set the MDB to have container managed transactions and I have added a LocalSessionFactoryBean to the Spring context, as you can see in the following listing:
As long as we have configured the application server's JDBC connections to be XA, with no other configuration, all Hibernate operations take part in the same global transaction. The container ensures this by handing out an XA enabled data source to Spring, which hands it out to Hibernate. So there is no problem of transaction atomicity. There is another problem however: Session propagation. Session propagation refers to using the same Hibernate session throughout the logical unit of work. With the previous configuration, a Hibernate Session is opened and closed (by Spring) for each operation. The session is flushed and the database connection is closed right before the session is closed. Although this works, it is not efficient. This is referred to as the "session-per-operation" anti pattern. Most importantly, lazy loading with proxies outside of "doInHibernate" will throw an Exception since the session is closed immediately after the operation. So, what can we do to fix this?
In order to answer this question we need to know what Hibernate does by itself (without Spring) for transaction management and session propagation and what Spring adds to these. If you prefer, you can skip this section and jump directly to the conclusion section to see the final configuration.
A. Transaction management with Hibernate
Firstly, let's examine the transaction handling options in Hibernate without Spring So, no Spring and no sessionFactory.getCurrentSession() calls!
1. Using JTA directly (UserTransaction)
Transaction handling is done outside of Hibernate. A typical hibernate operation will be like the following:
To enable this option we need to set the following Hibernate properties
Why are the options needed? If you want to have aggressive connection release, that is the database connection is automatically released after each Hibernate operation, you need the factory_class option together with release_mode=auto. The manager_lookup_class option configures SessionFactory with the JTA transaction manager. When Hibernate detects this transaction manager it registers some callbacks with it so it can do perform cleanup work when the transaction ends. The callbacks also allow us to set two additional Hibernate properties so that the session is flushed and closed automatically when the transaction ends: hibernate.transaction.flush_before_completion=true, hibernate.transaction.auto_close_session=true. As you will see soon, auto flush and auto close are not needed when adding Spring in the mix. Assuming we do not want aggressive connection release, factory_class and manager_lookup_class are probably not needed either when Spring is added. Spring documentation mentions that these two parameters need to be set in case we are getting "spurious application server warnings" (see section 12.2.10 in 2.0.x spring reference).
2. Using CMT
Transaction handling is again done outside of Hibernate (the container takes care of it). A typical hibernate operation will look like:
To enable this option we need to set the same Hibernate properties to corresponding values. Again, we can automate the flushing and closing of the session when the transaction ends by settings the same properties (flush_before_completion=true, auto_close_session=true). Java Persistence with Hibernate (the definitive book for Hibernate) says that in CMT the session is flushed and closed automatically, but I cannot find anywhere in the Hibernate-core code where this happens without setting the corresponding parameters or using getCurrentSession (as explained later using getCurrentSession enables auto flush and auto close). I need to examine this further. (TODO) Anyhow, since we are going to use Spring it does not matter anyway.
3) Using Hibernate Transaction API
Hibernate offers an API for programmatic transaction demarcation which works on top of either resource-local transactions (plain JDBC) or JTA global transactions. This option is not used with CMT. Even with programmatic transaction demarcation it cannot be used in combination with Hibernate's session propagation feature (getCurrentSession, see explanation below).
B. Session Propagation with Hibernate
Now, let's see what Hibernate (again without Spring) offers for session propagation. One simple method: SessionFactory.getCurrentSession(). You are supposed to call this method whenever you want to access a Hibernate Session instead of calling openSession. The first time you call this method, a new Session is opened and binded to the transaction (either JTA or not). Each successive time you call the method, it returns the same session. The session is auto flushed and auto closed when the transaction ends! This is possible thanks to the auto flush and auto close properties mentioned above which are automatically set to true when getCurrentSession opens a new Session. So, by replacing openSession with getCurrentSession we also don't need to flush/close the session.
C. Adding Spring on top
Now, what does Spring offer? Firstly, regarding transaction demarcation with JTA, instead of us getting the UserTransaction from JNDI, calling begin, commit, rollback, etc Spring's JtaTransactionManager does these things for us. In my case, for the reasons I explained above, I am not using transaction management at the Spring level.
Regarding session propagation, Spring offers the same feature that Hibernate offers with getCurrentSession. When using HibernateTemplate to perform operations with Hibernate, Spring uses its own session management with SessionFactoryUtils. SessionFactoryUtils also offers automatic flushing and closing of the session when the transaction ends without any additional Spring or Hibernate properties needed. Database connection release mode is by default set to on_close, meaning the database connection is closed when the session is closed. In case you are using getCurrentSession instead of HibernateTemplate Spring also overrides this method to use its own session management (this is what the exposeTransactionAwareSessionFactory parameter does).
So why is Spring session management (propagation) not working in my initial configuration above? The reason is because session propagation is enabled in Spring if either transaction demarcation is Spring driven, meaning if a PlatformTransactionManager is set (which in my case is not) or a JTA transaction manager is configured on the Hibernate SessionFactory. This is exactly what needs to be added and it can be done in 2 ways: either set the jtaTransactionManager Spring property on LocalSessionFactoryBean or set the hibernate.transaction.manager_lookup_class property mentioned above.
As soon as Spring detects a transaction manager configured on the SessionFactory it binds the Session to the current transaction. Also, it registers a callback so that it can flush and close the session when the transaction commits (in the CMT case, the container commits the transaction and calls back the Spring code).
Regarding database connection release, Spring sets the release mode to on_close by default. In CMT, aggressive connection release is more appropriate. To achieve this, we have to set the Hibernate property hibernate.connection.release_mode to auto. This will override the Spring setting. The possible values are on_close, after_statement, after_transaction and auto. We want after_statement, why set it to auto? Because if we set it to after_statement Hibernate (SettingsFactory.buildSettings) asks the ConnectionProvider whether it supports aggressive connection release. ConnectionProvider is set to LocalDataSourceConnectionProvider by Spring and returns false. If we set release_mode to auto, ConnectionProvider is not consulted, but instead the setting is taken from the transaction factory (CMTTransactionFactory in our case), which returns after_statement.
D. Conclusion
So the final configuration becomes:
With this configuration the following points apply:
This setup achieves the session-per-request pattern for CMT beans.
Integrating Spring and Hibernate is quite well documented in the Spring reference and other books. However, there is a special case which involves an EJB with Container Managed Transactions (CMT) --> calling Spring --> calling Hibernate. This has been a little confusing for me.
So what is the case? Suppose we have a Message Driven Bean (MDB) and we want to do some work with Hibernate upon reception of a message. The container dequeues the message under the covers before control is handed over to our MDB. We want this dequeue operation to be part of the transaction. Using CMT is the most straightforward configuration that comes to mind. Another option, which achieves the same results but without including the dequeue operation in the actual JTA transaction is described in this article.
Since we am using CMT, we are not going to use any transaction management at the Spring level (PlatformTransactionManager). We are letting the container do all transaction demarcation. Why is that? According to the J2EE spec, CMT beans are not supposed to interact directly with JTA (not event through UserTransaction). This applies to Spring's JtaTransactionManager within CMT EJB. Although this actually works in many application servers, it does violate the spec. Therefore, in this scenario we are not going to use Spring transaction management.
At this point, I have set the MDB to have container managed transactions and I have added a LocalSessionFactoryBean to the Spring context, as you can see in the following listing:
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/myDS" resource-ref="true"/>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource">
<property name="mappingResources" ref="...">
<property name="hibernateProperties">
<props>
...hibernate properties unrelated to transactions, sessions, connections...
</props>
</property>
</bean>
As long as we have configured the application server's JDBC connections to be XA, with no other configuration, all Hibernate operations take part in the same global transaction. The container ensures this by handing out an XA enabled data source to Spring, which hands it out to Hibernate. So there is no problem of transaction atomicity. There is another problem however: Session propagation. Session propagation refers to using the same Hibernate session throughout the logical unit of work. With the previous configuration, a Hibernate Session is opened and closed (by Spring) for each operation. The session is flushed and the database connection is closed right before the session is closed. Although this works, it is not efficient. This is referred to as the "session-per-operation" anti pattern. Most importantly, lazy loading with proxies outside of "doInHibernate" will throw an Exception since the session is closed immediately after the operation. So, what can we do to fix this?
In order to answer this question we need to know what Hibernate does by itself (without Spring) for transaction management and session propagation and what Spring adds to these. If you prefer, you can skip this section and jump directly to the conclusion section to see the final configuration.
A. Transaction management with Hibernate
Firstly, let's examine the transaction handling options in Hibernate without Spring So, no Spring and no sessionFactory.getCurrentSession() calls!
1. Using JTA directly (UserTransaction)
Transaction handling is done outside of Hibernate. A typical hibernate operation will be like the following:
//get the UserTransaction
userTransaction.begin();
sessionFactry.openSession();
//do some work with session
session.flush();
userTransaction.commit/rollback();
session.close();
To enable this option we need to set the following Hibernate properties
<prop key="hibernate.transaction.factory_class">org.hibernate.transaction.JTATransactionFactory</prop>
<prop key="hibernate.transaction.manager_lookup_class">...(depends on your application server, check documentation)...</prop>
Why are the options needed? If you want to have aggressive connection release, that is the database connection is automatically released after each Hibernate operation, you need the factory_class option together with release_mode=auto. The manager_lookup_class option configures SessionFactory with the JTA transaction manager. When Hibernate detects this transaction manager it registers some callbacks with it so it can do perform cleanup work when the transaction ends. The callbacks also allow us to set two additional Hibernate properties so that the session is flushed and closed automatically when the transaction ends: hibernate.transaction.flush_before_completion=true, hibernate.transaction.auto_close_session=true. As you will see soon, auto flush and auto close are not needed when adding Spring in the mix. Assuming we do not want aggressive connection release, factory_class and manager_lookup_class are probably not needed either when Spring is added. Spring documentation mentions that these two parameters need to be set in case we are getting "spurious application server warnings" (see section 12.2.10 in 2.0.x spring reference).
2. Using CMT
Transaction handling is again done outside of Hibernate (the container takes care of it). A typical hibernate operation will look like:
sessionFactry.openSession();
//do some work with session
session.flush();
session.close();
To enable this option we need to set the same Hibernate properties to corresponding values. Again, we can automate the flushing and closing of the session when the transaction ends by settings the same properties (flush_before_completion=true, auto_close_session=true). Java Persistence with Hibernate (the definitive book for Hibernate) says that in CMT the session is flushed and closed automatically, but I cannot find anywhere in the Hibernate-core code where this happens without setting the corresponding parameters or using getCurrentSession (as explained later using getCurrentSession enables auto flush and auto close). I need to examine this further. (TODO) Anyhow, since we are going to use Spring it does not matter anyway.
<prop key="hibernate.transaction.factory_class">org.hibernate.transaction.CMTTransactionFactory</prop>
<prop key="hibernate.transaction.manager_lookup_class">...(depends on your application server, check documentation)...</prop>
3) Using Hibernate Transaction API
Hibernate offers an API for programmatic transaction demarcation which works on top of either resource-local transactions (plain JDBC) or JTA global transactions. This option is not used with CMT. Even with programmatic transaction demarcation it cannot be used in combination with Hibernate's session propagation feature (getCurrentSession, see explanation below).
B. Session Propagation with Hibernate
Now, let's see what Hibernate (again without Spring) offers for session propagation. One simple method: SessionFactory.getCurrentSession(). You are supposed to call this method whenever you want to access a Hibernate Session instead of calling openSession. The first time you call this method, a new Session is opened and binded to the transaction (either JTA or not). Each successive time you call the method, it returns the same session. The session is auto flushed and auto closed when the transaction ends! This is possible thanks to the auto flush and auto close properties mentioned above which are automatically set to true when getCurrentSession opens a new Session. So, by replacing openSession with getCurrentSession we also don't need to flush/close the session.
C. Adding Spring on top
Now, what does Spring offer? Firstly, regarding transaction demarcation with JTA, instead of us getting the UserTransaction from JNDI, calling begin, commit, rollback, etc Spring's JtaTransactionManager does these things for us. In my case, for the reasons I explained above, I am not using transaction management at the Spring level.
Regarding session propagation, Spring offers the same feature that Hibernate offers with getCurrentSession. When using HibernateTemplate to perform operations with Hibernate, Spring uses its own session management with SessionFactoryUtils. SessionFactoryUtils also offers automatic flushing and closing of the session when the transaction ends without any additional Spring or Hibernate properties needed. Database connection release mode is by default set to on_close, meaning the database connection is closed when the session is closed. In case you are using getCurrentSession instead of HibernateTemplate Spring also overrides this method to use its own session management (this is what the exposeTransactionAwareSessionFactory parameter does).
So why is Spring session management (propagation) not working in my initial configuration above? The reason is because session propagation is enabled in Spring if either transaction demarcation is Spring driven, meaning if a PlatformTransactionManager is set (which in my case is not) or a JTA transaction manager is configured on the Hibernate SessionFactory. This is exactly what needs to be added and it can be done in 2 ways: either set the jtaTransactionManager Spring property on LocalSessionFactoryBean or set the hibernate.transaction.manager_lookup_class property mentioned above.
As soon as Spring detects a transaction manager configured on the SessionFactory it binds the Session to the current transaction. Also, it registers a callback so that it can flush and close the session when the transaction commits (in the CMT case, the container commits the transaction and calls back the Spring code).
Regarding database connection release, Spring sets the release mode to on_close by default. In CMT, aggressive connection release is more appropriate. To achieve this, we have to set the Hibernate property hibernate.connection.release_mode to auto. This will override the Spring setting. The possible values are on_close, after_statement, after_transaction and auto. We want after_statement, why set it to auto? Because if we set it to after_statement Hibernate (SettingsFactory.buildSettings) asks the ConnectionProvider whether it supports aggressive connection release. ConnectionProvider is set to LocalDataSourceConnectionProvider by Spring and returns false. If we set release_mode to auto, ConnectionProvider is not consulted, but instead the setting is taken from the transaction factory (CMTTransactionFactory in our case), which returns after_statement.
D. Conclusion
So the final configuration becomes:
<jee:jndi-lookup id="dataSource" jndi-name="jdbc/myDS" resource-ref="true"/>
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mappingResources" ref="..."/>
<property name="hibernateProperties">
<props>
<prop key="hibernate.transaction.factory_class">org.hibernate.transaction.CMTTransactionFactory</prop>
<prop key="hibernate.transaction.manager_lookup_class">...(depends on you application server, check documentation)...</prop>
<prop key="hibernate.connection.release_mode">auto</prop>
...hibernate properties unrelated to transactions, sessions, connections...
</props>
</property>
</bean>
With this configuration the following points apply:
- The container begins and ends the transaction (CMT)
- Spring gives database connections to Hibernate from the JNDI data source we have configured (It sets ConnectionManager to LocalDataSourceConnectionManager for that). These connections are JTA enabled and enlisted in the global transaction by the container's transaction manager.
- The connection release mode is set to after_statement because release_mode=auto and CMTTransactionManager sets it to after_statement.
- The SessionFactory gets configured with a JTA transaction manager because of manager_lookup_class.
- The currentSessionContext of Hibernate is set to JTASessionContext, but that doesn't matter anyway because Spring overrides getCurrentSession() to use it's own session management.
- When a session is opened through Spring (either though HibernateTemplate or getCurrentSession), because of point 4, Spring detects that SessionFactory has a transaction manager configured and binds the session to the current global transaction. It registers callbacks for auto flushing and closing when the transaction ends. At the same time, also because Hibernate SessionFactory is configured with a transaction manager, Hibernate also registers a callback: the CacheSynchronization. Spring is aware of this callback too and makes sure no conflicts occur.
This setup achieves the session-per-request pattern for CMT beans.
Εγγραφή σε:
Αναρτήσεις (Atom)
